Compare commits
94 Commits
v1.10.12
...
t3chguy/ci
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7052a5c5f | ||
|
|
3c170bbe96 | ||
|
|
2a9587d4ff | ||
|
|
69426387dc | ||
|
|
76c9535255 | ||
|
|
97fb7f0235 | ||
|
|
41f05541ed | ||
|
|
4a91c172b2 | ||
|
|
7c8ded1526 | ||
|
|
e92d44eb56 | ||
|
|
70a247446e | ||
|
|
5ade461ea5 | ||
|
|
9df5bf17f4 | ||
|
|
51ed7784d5 | ||
|
|
359e0e205f | ||
|
|
89bffd132a | ||
|
|
9c92f55afd | ||
|
|
745140e9e7 | ||
|
|
863e5f6c78 | ||
|
|
4e6836d00e | ||
|
|
1cdbcf29ce | ||
|
|
c46c112c32 | ||
|
|
d5cf793fad | ||
|
|
bf1f297bed | ||
|
|
1c8b52e1cd | ||
|
|
7c949f9f5a | ||
|
|
11a3011cbd | ||
|
|
6c7f663983 | ||
|
|
57499c721f | ||
|
|
61a8b6fbc6 | ||
|
|
095f2733f7 | ||
|
|
b9af13b533 | ||
|
|
fab52795e3 | ||
|
|
b2d057b7c3 | ||
|
|
d36dcd2766 | ||
|
|
ff7398b21f | ||
|
|
f906cc3067 | ||
|
|
06349e4a9c | ||
|
|
0039572231 | ||
|
|
676cf4fd39 | ||
|
|
91805c9f44 | ||
|
|
fa2f438a89 | ||
|
|
20cc77401c | ||
|
|
56f3afc7f8 | ||
|
|
b3ef2c179a | ||
|
|
aca0346f4e | ||
|
|
88b5368f72 | ||
|
|
ca98529bd2 | ||
|
|
0292f66365 | ||
|
|
6c87e294c4 | ||
|
|
f080b1fb27 | ||
|
|
f2c8e89053 | ||
|
|
479d4bf64d | ||
|
|
d7a6fbc5fe | ||
|
|
3a5091f2d8 | ||
|
|
915c839148 | ||
|
|
cdf95ab8b7 | ||
|
|
1783645108 | ||
|
|
31b43542cd | ||
|
|
a94323fc36 | ||
|
|
e749bb3ce3 | ||
|
|
a885de4eda | ||
|
|
9cb0185ec2 | ||
|
|
abb31c9b01 | ||
|
|
bf1e8a07c1 | ||
|
|
092f919e60 | ||
|
|
b98e605574 | ||
|
|
8bb13e8d2d | ||
|
|
b18c944b42 | ||
|
|
0bbad38929 | ||
|
|
a431363768 | ||
|
|
2d7bada5c4 | ||
|
|
0e22503860 | ||
|
|
a340358551 | ||
|
|
e53f2695a4 | ||
|
|
0a239a30fa | ||
|
|
dab1488ffe | ||
|
|
61f8ec5e10 | ||
|
|
7a01c6c61b | ||
|
|
0ab7cd05c7 | ||
|
|
fa28d2400b | ||
|
|
7cc52e68d1 | ||
|
|
8bdd965122 | ||
|
|
418de7998a | ||
|
|
008889d2a8 | ||
|
|
b577d0f2f2 | ||
|
|
98733057a7 | ||
|
|
0cd6e02c99 | ||
|
|
449a0e64d9 | ||
|
|
89b3e4aaab | ||
|
|
fe8c583e09 | ||
|
|
5adf38c87f | ||
|
|
59d7265e69 | ||
|
|
446b510b82 |
@@ -21,3 +21,9 @@ insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{yml,yaml}]
|
||||
indent_size = 2
|
||||
|
||||
[package.json]
|
||||
indent_size = 2
|
||||
|
||||
@@ -30,6 +30,8 @@ module.exports = {
|
||||
|
||||
// We disable this while we're transitioning
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
// We're okay with assertion errors when we ask for them
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
|
||||
// Ban matrix-js-sdk/src imports in favour of matrix-js-sdk/src/matrix imports to prevent unleashing hell.
|
||||
"no-restricted-imports": ["error", {
|
||||
|
||||
33
.github/workflows/build.yaml
vendored
Normal file
33
.github/workflows/build.yaml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ master ]
|
||||
# develop pushes and repository_dispatch handled in build_develop.yaml
|
||||
env:
|
||||
# These must be set for fetchdep.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
jobs:
|
||||
build:
|
||||
name: "Build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Build & Package
|
||||
run: "./scripts/ci_package.sh"
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: previewbuild
|
||||
path: dist/*.tar.gz
|
||||
retention-days: 28
|
||||
26
.github/workflows/build.yml
vendored
Normal file
26
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Build and Package
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ master ]
|
||||
# develop pushes and repository_dispatch handled in build_develop.yaml
|
||||
env:
|
||||
# These must be set for fetchdep.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
jobs:
|
||||
build:
|
||||
name: "Build"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Build & Package
|
||||
run: "./scripts/ci_package.sh"
|
||||
38
.github/workflows/build_develop.yaml
vendored
Normal file
38
.github/workflows/build_develop.yaml
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# Separate to the main build workflow for access to develop
|
||||
# environment secrets, largely similar to build.yaml.
|
||||
name: Build develop
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
jobs:
|
||||
build:
|
||||
name: "Build & Upload source maps to Sentry"
|
||||
runs-on: ubuntu-latest
|
||||
environment: develop
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Build, Package & Upload sourcemaps
|
||||
run: "./scripts/ci_package.sh"
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: sentry
|
||||
SENTRY_PROJECT: riot-web
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: previewbuild
|
||||
path: dist/*.tar.gz
|
||||
retention-days: 1
|
||||
31
.github/workflows/build_develop.yml
vendored
Normal file
31
.github/workflows/build_develop.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# Separate to the main build workflow for access to develop
|
||||
# environment secrets, largely similar to build.yaml.
|
||||
name: Build and Package develop
|
||||
on:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
jobs:
|
||||
build:
|
||||
name: "Build & Upload source maps to Sentry"
|
||||
runs-on: ubuntu-latest
|
||||
environment: develop
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Build, Package & Upload sourcemaps
|
||||
run: "./scripts/ci_package.sh"
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: sentry
|
||||
SENTRY_PROJECT: riot-web
|
||||
64
.github/workflows/deploy_develop.yaml
vendored
Normal file
64
.github/workflows/deploy_develop.yaml
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
# Triggers after the Build has finished,
|
||||
# because artifacts are not externally available
|
||||
# until the end of their workflow.
|
||||
name: Deploy develop.element.io
|
||||
concurrency: deploy_develop
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "Build develop" ]
|
||||
types:
|
||||
- completed
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment: develop
|
||||
if: github.event.workflow_run.conclusion == 'success'
|
||||
steps:
|
||||
- name: Find Artifact ID
|
||||
uses: actions/github-script@v3.1.0
|
||||
id: find_artifact
|
||||
with:
|
||||
result-encoding: string
|
||||
script: |
|
||||
const artifacts = await github.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "previewbuild"
|
||||
})[0];
|
||||
const download = await github.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
return download.url;
|
||||
|
||||
- name: Create Deployment
|
||||
uses: bobheadxi/deployments@v1
|
||||
id: deployment
|
||||
with:
|
||||
step: start
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
env: Develop
|
||||
ref: ${{ github.head_ref }}
|
||||
|
||||
- name: Notify the redeploy script
|
||||
uses: distributhor/workflow-webhook@v2
|
||||
env:
|
||||
webhook_url: ${{ secrets.DEVELOP_DEPLOY_WEBHOOK_URL }}
|
||||
webhook_secret: ${{ secrets.DEVELOP_DEPLOY_WEBHOOK_SECRET }}
|
||||
data: '{"url": "${{ steps.find_artifact.outputs.result }}"}'
|
||||
|
||||
- name: Update deployment status
|
||||
uses: bobheadxi/deployments@v1
|
||||
if: always()
|
||||
with:
|
||||
step: finish
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
status: ${{ job.status }}
|
||||
env: ${{ steps.deployment.outputs.env }}
|
||||
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
|
||||
env_url: https://develop.element.io
|
||||
148
.github/workflows/issue_closed.yml
vendored
Normal file
148
.github/workflows/issue_closed.yml
vendored
Normal file
@@ -0,0 +1,148 @@
|
||||
# For duplicate issues, ensure the close type is right (not planned), update it if not
|
||||
# For all closed (completed) issues, cascade the closure onto any referenced rageshakes
|
||||
# For all closed (not planned) issues, comment on rageshakes to move them into the canonical issue if one exists
|
||||
on:
|
||||
issues:
|
||||
types: [ closed ]
|
||||
jobs:
|
||||
tidy:
|
||||
name: Tidy closed issues
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
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 }}
|
||||
script: |
|
||||
const variables = {
|
||||
owner: context.repo.owner,
|
||||
name: context.repo.repo,
|
||||
number: context.issue.number,
|
||||
};
|
||||
|
||||
const query = `query($owner:String!, $name:String!, $number:Int!) {
|
||||
repository(owner: $owner, name: $name) {
|
||||
issue(number: $number) {
|
||||
stateReason,
|
||||
timelineItems(first: 100, itemTypes: [MARKED_AS_DUPLICATE_EVENT, UNMARKED_AS_DUPLICATE_EVENT, CROSS_REFERENCED_EVENT]) {
|
||||
edges {
|
||||
node {
|
||||
__typename
|
||||
... on MarkedAsDuplicateEvent {
|
||||
canonical {
|
||||
... on Issue {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
... on PullRequest {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
... on UnmarkedAsDuplicateEvent {
|
||||
canonical {
|
||||
... on Issue {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
... on PullRequest {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
... on CrossReferencedEvent {
|
||||
source {
|
||||
... on Issue {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
... on PullRequest {
|
||||
repository {
|
||||
nameWithOwner
|
||||
}
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
const result = await github.graphql(query, variables);
|
||||
const { stateReason, timelineItems: { edges } } = result.repository.issue;
|
||||
|
||||
const RAGESHAKE_OWNER = "matrix-org";
|
||||
const RAGESHAKE_REPO = "element-web-rageshakes";
|
||||
const rageshakes = new Set();
|
||||
const duplicateOf = new Set();
|
||||
|
||||
console.log("Edges: ", JSON.stringify(edges));
|
||||
|
||||
for (const { node } of edges) {
|
||||
switch(node.__typename) {
|
||||
case "MarkedAsDuplicateEvent":
|
||||
duplicateOf.add(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
|
||||
break;
|
||||
case "UnmarkedAsDuplicateEvent":
|
||||
duplicateOf.remove(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
|
||||
break;
|
||||
case "CrossReferencedEvent":
|
||||
if (node.source.repository.nameWithOwner === (RAGESHAKE_OWNER + "/" + RAGESHAKE_REPO)) {
|
||||
rageshakes.add(node.source.number);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Duplicate of: ", duplicateOf);
|
||||
console.log("Found rageshakes: ", rageshakes);
|
||||
|
||||
if (duplicateOf.size) {
|
||||
const body = Array.from(duplicateOf).join("\n");
|
||||
|
||||
// Comment on all rageshakes to create relationship to the issue this was closed as duplicate of
|
||||
for (const rageshake of rageshakes) {
|
||||
github.rest.issues.createComment({
|
||||
owner: RAGESHAKE_OWNER,
|
||||
repo: RAGESHAKE_REPO,
|
||||
issue_number: rageshake,
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// This issue was closed, close all related rageshakes
|
||||
for (const rageshake of rageshakes) {
|
||||
github.rest.issues.update({
|
||||
owner: RAGESHAKE_OWNER,
|
||||
repo: RAGESHAKE_REPO,
|
||||
issue_number: rageshake,
|
||||
state: "closed",
|
||||
});
|
||||
}
|
||||
}
|
||||
12
.github/workflows/preview_changelog.yaml
vendored
12
.github/workflows/preview_changelog.yaml
vendored
@@ -1,12 +0,0 @@
|
||||
name: Preview Changelog
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, edited, labeled ]
|
||||
jobs:
|
||||
changelog:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Preview Changelog
|
||||
uses: matrix-org/allchange@main
|
||||
with:
|
||||
ghToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
26
.github/workflows/pull_request.yaml
vendored
Normal file
26
.github/workflows/pull_request.yaml
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Pull Request
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [ opened, edited, labeled, unlabeled, synchronize ]
|
||||
concurrency: ${{ github.workflow }}-${{ github.event.pull_request.head.ref }}
|
||||
jobs:
|
||||
changelog:
|
||||
name: Preview Changelog
|
||||
if: github.event.action != 'synchronize'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: matrix-org/allchange@main
|
||||
with:
|
||||
ghToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
enforce-label:
|
||||
name: Enforce Labels
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: yogevbd/enforce-label-action@2.1.0
|
||||
with:
|
||||
REQUIRED_LABELS_ANY: "T-Defect,T-Enhancement,T-Task"
|
||||
BANNED_LABELS: "X-Blocked"
|
||||
BANNED_LABELS_DESCRIPTION: "Preventing merge whilst PR is marked blocked!"
|
||||
26
.github/workflows/sentry-sourcemaps.yaml
vendored
26
.github/workflows/sentry-sourcemaps.yaml
vendored
@@ -1,26 +0,0 @@
|
||||
name: Upload Sentry Sourcemaps
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
jobs:
|
||||
upload-sentry-sourcemaps:
|
||||
runs-on: ubuntu-latest
|
||||
environment: develop
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '14'
|
||||
cache: 'yarn'
|
||||
- run: ./scripts/fetch-develop.deps.sh --depth 1
|
||||
- run: yarn install
|
||||
- run: ./scripts/ci_package.sh
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: sentry
|
||||
SENTRY_PROJECT: riot-web
|
||||
15
.github/workflows/sonarqube.yml
vendored
Normal file
15
.github/workflows/sonarqube.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: SonarQube
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [ "Tests" ]
|
||||
types:
|
||||
- completed
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
sonarqube:
|
||||
name: 🩻 SonarQube
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop
|
||||
secrets:
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
65
.github/workflows/static_analysis.yaml
vendored
Normal file
65
.github/workflows/static_analysis.yaml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
name: Static Analysis
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
env:
|
||||
# These must be set for fetchdep.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
jobs:
|
||||
ts_lint:
|
||||
name: "Typescript Syntax Check"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Typecheck
|
||||
run: "yarn run lint:types"
|
||||
|
||||
i18n_lint:
|
||||
name: "i18n Check"
|
||||
uses: matrix-org/matrix-react-sdk/.github/workflows/i18n_check.yml@develop
|
||||
|
||||
js_lint:
|
||||
name: "ESLint"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --pure-lockfile"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:js"
|
||||
|
||||
style_lint:
|
||||
name: "Style Lint"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
# Does not need branch matching as only analyses this layer
|
||||
- name: Install Deps
|
||||
run: "yarn install --pure-lockfile"
|
||||
|
||||
- name: Run Linter
|
||||
run: "yarn run lint:style"
|
||||
27
.github/workflows/test.yaml
vendored
Normal file
27
.github/workflows/test.yaml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Test
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ master, develop ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
env:
|
||||
# These must be set for fetchdep.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
jobs:
|
||||
test:
|
||||
name: "Test"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Run Tests
|
||||
run: "yarn test"
|
||||
37
.github/workflows/tests.yaml
vendored
Normal file
37
.github/workflows/tests.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Tests
|
||||
on:
|
||||
pull_request: { }
|
||||
push:
|
||||
branches: [ develop, master ]
|
||||
repository_dispatch:
|
||||
types: [ element-web-notify ]
|
||||
env:
|
||||
# These must be set for fetchdep.sh to get the right branch
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
jobs:
|
||||
jest:
|
||||
name: Jest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Yarn cache
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Dependencies
|
||||
run: "./scripts/layered.sh"
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: "yarn coverage --ci"
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: coverage
|
||||
path: |
|
||||
coverage
|
||||
!coverage/lcov-report
|
||||
18
.github/workflows/triage-assigned.yml
vendored
Normal file
18
.github/workflows/triage-assigned.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Move issued assigned to specific team members to their boards
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [ assigned ]
|
||||
|
||||
jobs:
|
||||
web-app-team:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
contains(github.event.issue.assignees.*.login, 't3chguy') ||
|
||||
contains(github.event.issue.assignees.*.login, 'turt2live')
|
||||
steps:
|
||||
- uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
|
||||
with:
|
||||
project: Web App Team
|
||||
column: Ready
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
2
.github/workflows/triage-incoming.yml
vendored
2
.github/workflows/triage-incoming.yml
vendored
@@ -2,7 +2,7 @@ name: Move new issues into Issue triage board
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
types: [ opened ]
|
||||
|
||||
jobs:
|
||||
automate-project-columns:
|
||||
|
||||
58
.github/workflows/triage-labelled.yml
vendored
58
.github/workflows/triage-labelled.yml
vendored
@@ -3,21 +3,21 @@ name: Move labelled issues to correct projects
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
|
||||
jobs:
|
||||
apply_Z-Labs_label:
|
||||
name: Add Z-Labs label for features behind labs flags
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms')
|
||||
contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms')
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
@@ -44,14 +44,14 @@ 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') &&
|
||||
(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'))
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
name: X-Needs-Product to Design project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
||||
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
id: add_to_project
|
||||
@@ -99,13 +99,9 @@ jobs:
|
||||
name: Delight issues to project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Spaces') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Space-Settings') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Subspaces') ||
|
||||
contains(github.event.issue.labels.*.name, 'Team: Delight') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-NewUserJourney')
|
||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience') ||
|
||||
contains(github.event.issue.labels.*.name, 'Team: Delight') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-NewUserJourney')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
@@ -128,7 +124,7 @@ jobs:
|
||||
name: A-Voice Messages to voice message board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
|
||||
contains(github.event.issue.labels.*.name, 'A-Voice Messages')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
@@ -151,7 +147,7 @@ jobs:
|
||||
name: A-Threads to Thread board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Threads')
|
||||
contains(github.event.issue.labels.*.name, 'A-Threads')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
@@ -174,7 +170,7 @@ jobs:
|
||||
name: A-Message-Bubbles to Message bubbles board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
@@ -197,7 +193,7 @@ jobs:
|
||||
name: Z-FTUE issues to the FTUE project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Z-FTUE')
|
||||
contains(github.event.issue.labels.*.name, 'Z-FTUE')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
@@ -220,7 +216,7 @@ jobs:
|
||||
name: Z-WTF issues to the WTF project board
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Z-WTF')
|
||||
contains(github.event.issue.labels.*.name, 'Z-WTF')
|
||||
steps:
|
||||
- uses: octokit/graphql-action@v2.x
|
||||
with:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Move pull requests asking for review to the relevant project
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [review_requested]
|
||||
types: [ review_requested ]
|
||||
|
||||
jobs:
|
||||
add_design_pr_to_project:
|
||||
|
||||
54
.github/workflows/triage-priority-bugs.yml
vendored
54
.github/workflows/triage-priority-bugs.yml
vendored
@@ -5,49 +5,23 @@ on:
|
||||
types: [labeled, unlabeled]
|
||||
|
||||
jobs:
|
||||
p1_issues_to_team_workboard:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
(!contains(github.event.issue.labels.*.name, 'A-E2EE') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-E2EE-Cross-Signing') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-E2EE-Key-Backup') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-E2EE-SAS-Verification') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-Spaces') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-Spaces-Settings') &&
|
||||
!contains(github.event.issue.labels.*.name, 'A-Subspaces')) &&
|
||||
(contains(github.event.issue.labels.*.name, 'T-Defect') &&
|
||||
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'))
|
||||
steps:
|
||||
- uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
|
||||
with:
|
||||
project: Web App Team
|
||||
column: P1
|
||||
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
P1_issues_to_crypto_team_workboard:
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
contains(github.event.issue.labels.*.name, 'Z-UISI') ||
|
||||
(contains(github.event.issue.labels.*.name, 'A-E2EE') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Cross-Signing') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Key-Backup') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-SAS-Verification')) &&
|
||||
(contains(github.event.issue.labels.*.name, 'T-Defect') &&
|
||||
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, 'Z-UISI') ||
|
||||
(contains(github.event.issue.labels.*.name, 'A-E2EE') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Cross-Signing') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Key-Backup') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-SAS-Verification')) &&
|
||||
(contains(github.event.issue.labels.*.name, 'T-Defect') &&
|
||||
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'))
|
||||
steps:
|
||||
- uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
|
||||
with:
|
||||
|
||||
52
.github/workflows/triage-unlabelled.yml
vendored
52
.github/workflows/triage-unlabelled.yml
vendored
@@ -2,15 +2,15 @@ name: Move unlabelled from needs info columns to triaged
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [unlabeled]
|
||||
types: [ unlabeled ]
|
||||
|
||||
jobs:
|
||||
Move_Unabeled_Issue_On_Project_Board:
|
||||
name: Move no longer X-Needs-Info issues to Triaged
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
${{
|
||||
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
||||
${{
|
||||
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
||||
env:
|
||||
BOARD_NAME: "Issue triage"
|
||||
OWNER: ${{ github.repository_owner }}
|
||||
@@ -19,16 +19,24 @@ jobs:
|
||||
steps:
|
||||
- name: Check if issue is already in "${{ env.BOARD_NAME }}"
|
||||
run: |
|
||||
if curl -i -H 'Content-Type: application/json' -H "Authorization: bearer ${{ secrets.GITHUB_TOKEN }}" -X POST -d '{"query": "query($issue: Int!, $owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { issue(number: $issue) { projectCards { nodes { project { name } } } } } } ", "variables" : "{ \"issue\": '${ISSUE}', \"owner\": \"'${OWNER}'\", \"repo\": \"'${REPO}'\" }" }' https://api.github.com/graphql | grep "\b$BOARD_NAME\b"; then
|
||||
echo "Issue is already in Project '$BOARD_NAME', proceeding";
|
||||
echo "ALREADY_IN_BOARD=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Issue is not in project '$BOARD_NAME', cancelling this workflow"
|
||||
echo "ALREADY_IN_BOARD=false" >> $GITHUB_ENV
|
||||
json=$(curl -s -H 'Content-Type: application/json' -H "Authorization: bearer ${{ secrets.GITHUB_TOKEN }}" -X POST -d '{"query": "query($issue: Int!, $owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { issue(number: $issue) { projectCards { nodes { project { name } isArchived } } } } } ", "variables" : "{ \"issue\": '${ISSUE}', \"owner\": \"'${OWNER}'\", \"repo\": \"'${REPO}'\" }" }' https://api.github.com/graphql)
|
||||
if echo $json | jq '.data.repository.issue.projectCards.nodes | length'; then
|
||||
if [[ $(echo $json | jq '.data.repository.issue.projectCards.nodes[0].project.name') =~ "${BOARD_NAME}" ]]; then
|
||||
if [[ $(echo $json | jq '.data.repository.issue.projectCards.nodes[0].isArchived') == 'true' ]]; then
|
||||
echo "Issue is already in Project '$BOARD_NAME', but is archived - skipping workflow";
|
||||
echo "SKIP_ACTION=true" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Issue is already in Project '$BOARD_NAME', proceeding";
|
||||
echo "ALREADY_IN_BOARD=true" >> $GITHUB_ENV
|
||||
fi
|
||||
else
|
||||
echo "Issue is not in project '$BOARD_NAME', cancelling this workflow"
|
||||
echo "ALREADY_IN_BOARD=false" >> $GITHUB_ENV
|
||||
fi
|
||||
fi
|
||||
- name: Move issue
|
||||
uses: alex-page/github-project-automation-plus@bb266ff4dde9242060e2d5418e120a133586d488
|
||||
if: ${{ env.ALREADY_IN_BOARD == 'true' }}
|
||||
if: ${{ env.ALREADY_IN_BOARD == 'true' && env.SKIP_ACTION != 'true' }}
|
||||
with:
|
||||
project: Issue triage
|
||||
column: Triaged
|
||||
@@ -38,18 +46,18 @@ jobs:
|
||||
name: Remove Z-Labs label when features behind labs flags are removed
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
!(contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Threads') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Polls') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms')) &&
|
||||
contains(github.event.issue.labels.*.name, 'Z-Labs')
|
||||
!(contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Threads') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Polls') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Message-Bubbles') ||
|
||||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
|
||||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms')) &&
|
||||
contains(github.event.issue.labels.*.name, 'Z-Labs')
|
||||
steps:
|
||||
- uses: actions/github-script@v5
|
||||
with:
|
||||
|
||||
8
.github/workflows/upgrade_dependencies.yml
vendored
Normal file
8
.github/workflows/upgrade_dependencies.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
name: Upgrade Dependencies
|
||||
on:
|
||||
workflow_dispatch: { }
|
||||
jobs:
|
||||
upgrade:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -23,3 +23,6 @@ electron/pub
|
||||
.vscode
|
||||
.vscode/
|
||||
.env
|
||||
/coverage
|
||||
/scripts/extracted/
|
||||
/scripts/latest
|
||||
|
||||
93
CHANGELOG.md
93
CHANGELOG.md
@@ -1,3 +1,96 @@
|
||||
Changes in [1.10.13](https://github.com/vector-im/element-web/releases/tag/v1.10.13) (2022-05-24)
|
||||
=================================================================================================
|
||||
|
||||
## ✨ Features
|
||||
* Go to space landing page when clicking on a selected space ([\#6442](https://github.com/matrix-org/matrix-react-sdk/pull/6442)). Fixes #20296.
|
||||
* Fall back to untranslated string rather than showing missing translation error ([\#8609](https://github.com/matrix-org/matrix-react-sdk/pull/8609)).
|
||||
* Show file name and size on images on hover ([\#6511](https://github.com/matrix-org/matrix-react-sdk/pull/6511)). Fixes #18197.
|
||||
* Iterate on search results for message bubbles ([\#7047](https://github.com/matrix-org/matrix-react-sdk/pull/7047)). Fixes #20315.
|
||||
* registration: redesign email verification page ([\#8554](https://github.com/matrix-org/matrix-react-sdk/pull/8554)). Fixes #21984.
|
||||
* Show full thread message in hover title on thread summary ([\#8568](https://github.com/matrix-org/matrix-react-sdk/pull/8568)). Fixes #22037.
|
||||
* Tweak video rooms copy ([\#8582](https://github.com/matrix-org/matrix-react-sdk/pull/8582)). Fixes #22176.
|
||||
* Live location share - beacon tooltip in maximised view ([\#8572](https://github.com/matrix-org/matrix-react-sdk/pull/8572)).
|
||||
* Add dialog to navigate long room topics ([\#8517](https://github.com/matrix-org/matrix-react-sdk/pull/8517)). Fixes #9623.
|
||||
* Change spaceroomfacepile tooltip if memberlist is shown ([\#8571](https://github.com/matrix-org/matrix-react-sdk/pull/8571)). Fixes #17406.
|
||||
* Improve message editing UI ([\#8483](https://github.com/matrix-org/matrix-react-sdk/pull/8483)). Fixes #9752 and #22108.
|
||||
* Make date changes more obvious ([\#6410](https://github.com/matrix-org/matrix-react-sdk/pull/6410)). Fixes #16221.
|
||||
* Enable forwarding static locations ([\#8553](https://github.com/matrix-org/matrix-react-sdk/pull/8553)).
|
||||
* Log `TimelinePanel` debugging info when opening the bug report modal ([\#8502](https://github.com/matrix-org/matrix-react-sdk/pull/8502)).
|
||||
* Improve welcome screen, add opt-out analytics ([\#8474](https://github.com/matrix-org/matrix-react-sdk/pull/8474)). Fixes #21946.
|
||||
* Converting selected text to MD link when pasting a URL ([\#8242](https://github.com/matrix-org/matrix-react-sdk/pull/8242)). Fixes #21634. Contributed by @Sinharitik589.
|
||||
* Support Inter on custom themes ([\#8399](https://github.com/matrix-org/matrix-react-sdk/pull/8399)). Fixes #16293.
|
||||
* Add a `Copy link` button to the right-click message context-menu labs feature ([\#8527](https://github.com/matrix-org/matrix-react-sdk/pull/8527)).
|
||||
* Move widget screenshots labs flag to devtools ([\#8522](https://github.com/matrix-org/matrix-react-sdk/pull/8522)).
|
||||
* Remove some labs features which don't get used or create maintenance burden: custom status, multiple integration managers, and do not disturb ([\#8521](https://github.com/matrix-org/matrix-react-sdk/pull/8521)).
|
||||
* Add a way to toggle `ScrollPanel` and `TimelinePanel` debug logs ([\#8513](https://github.com/matrix-org/matrix-react-sdk/pull/8513)).
|
||||
* Spaces: remove blue beta dot ([\#8511](https://github.com/matrix-org/matrix-react-sdk/pull/8511)). Fixes #22061.
|
||||
* Order new search dialog results by recency ([\#8444](https://github.com/matrix-org/matrix-react-sdk/pull/8444)).
|
||||
* Improve pills ([\#6398](https://github.com/matrix-org/matrix-react-sdk/pull/6398)). Fixes #16948 and #21281.
|
||||
* Add a way to maximize/pin widget from the PiP view ([\#7672](https://github.com/matrix-org/matrix-react-sdk/pull/7672)). Fixes #20723.
|
||||
* Iterate video room designs in labs ([\#8499](https://github.com/matrix-org/matrix-react-sdk/pull/8499)).
|
||||
* Improve UI/UX in calls ([\#7791](https://github.com/matrix-org/matrix-react-sdk/pull/7791)). Fixes #19937.
|
||||
* Add ability to change audio and video devices during a call ([\#7173](https://github.com/matrix-org/matrix-react-sdk/pull/7173)). Fixes #15595.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
* Fix video rooms sometimes connecting muted when they shouldn't ([\#22125](https://github.com/vector-im/element-web/pull/22125)).
|
||||
* Avoid flashing the 'join conference' button at the user in video rooms ([\#22120](https://github.com/vector-im/element-web/pull/22120)).
|
||||
* Fully close Jitsi conferences on errors ([\#22060](https://github.com/vector-im/element-web/pull/22060)).
|
||||
* Fix click behavior of notification badges on spaces ([\#8627](https://github.com/matrix-org/matrix-react-sdk/pull/8627)). Fixes #22241.
|
||||
* Add missing return values in Read Receipt animation code ([\#8625](https://github.com/matrix-org/matrix-react-sdk/pull/8625)). Fixes #22175.
|
||||
* Fix 'continue' button not working after accepting identity server terms of service ([\#8619](https://github.com/matrix-org/matrix-react-sdk/pull/8619)). Fixes #20003.
|
||||
* Proactively fix stuck devices in video rooms ([\#8587](https://github.com/matrix-org/matrix-react-sdk/pull/8587)). Fixes #22131.
|
||||
* Fix position of the message action bar on left side bubbles ([\#8398](https://github.com/matrix-org/matrix-react-sdk/pull/8398)). Fixes #21879. Contributed by @luixxiul.
|
||||
* Fix edge case thread summaries around events without a msgtype ([\#8576](https://github.com/matrix-org/matrix-react-sdk/pull/8576)).
|
||||
* Fix favourites metaspace not updating ([\#8594](https://github.com/matrix-org/matrix-react-sdk/pull/8594)). Fixes #22156.
|
||||
* Stop spaces from displaying as rooms in new breadcrumbs ([\#8595](https://github.com/matrix-org/matrix-react-sdk/pull/8595)). Fixes #22165.
|
||||
* Fix avatar position of hidden event on ThreadView ([\#8592](https://github.com/matrix-org/matrix-react-sdk/pull/8592)). Fixes #22199. Contributed by @luixxiul.
|
||||
* Fix MessageTimestamp position next to redacted messages on IRC/modern layout ([\#8591](https://github.com/matrix-org/matrix-react-sdk/pull/8591)). Fixes #22181. Contributed by @luixxiul.
|
||||
* Fix padding of messages in threads ([\#8574](https://github.com/matrix-org/matrix-react-sdk/pull/8574)). Contributed by @luixxiul.
|
||||
* Enable overflow of hidden events content ([\#8585](https://github.com/matrix-org/matrix-react-sdk/pull/8585)). Fixes #22187. Contributed by @luixxiul.
|
||||
* Increase composer line height to avoid cutting off emoji ([\#8583](https://github.com/matrix-org/matrix-react-sdk/pull/8583)). Fixes #22170.
|
||||
* Don't consider threads for breaking continuation until actually created ([\#8581](https://github.com/matrix-org/matrix-react-sdk/pull/8581)). Fixes #22164.
|
||||
* Fix displaying hidden events on threads ([\#8555](https://github.com/matrix-org/matrix-react-sdk/pull/8555)). Fixes #22058. Contributed by @luixxiul.
|
||||
* Fix button width and align 絵文字 (emoji) on the user panel ([\#8562](https://github.com/matrix-org/matrix-react-sdk/pull/8562)). Fixes #22142. Contributed by @luixxiul.
|
||||
* Standardise the margin for settings tabs ([\#7963](https://github.com/matrix-org/matrix-react-sdk/pull/7963)). Fixes #20767. Contributed by @yuktea.
|
||||
* Fix room history not being visible even if we have historical keys ([\#8563](https://github.com/matrix-org/matrix-react-sdk/pull/8563)). Fixes #16983.
|
||||
* Fix oblong avatars in video room lobbies ([\#8565](https://github.com/matrix-org/matrix-react-sdk/pull/8565)).
|
||||
* Update thread summary when latest event gets decrypted ([\#8564](https://github.com/matrix-org/matrix-react-sdk/pull/8564)). Fixes #22151.
|
||||
* Fix codepath which can wrongly cause automatic space switch from all rooms ([\#8560](https://github.com/matrix-org/matrix-react-sdk/pull/8560)). Fixes #21373.
|
||||
* Fix effect of URL preview toggle not updating live ([\#8561](https://github.com/matrix-org/matrix-react-sdk/pull/8561)). Fixes #22148.
|
||||
* Fix visual bugs on AccessSecretStorageDialog ([\#8160](https://github.com/matrix-org/matrix-react-sdk/pull/8160)). Fixes #19426. Contributed by @luixxiul.
|
||||
* Fix the width bounce of the clock on the AudioPlayer ([\#8320](https://github.com/matrix-org/matrix-react-sdk/pull/8320)). Fixes #21788. Contributed by @luixxiul.
|
||||
* Hide the verification left stroke only on the thread list ([\#8525](https://github.com/matrix-org/matrix-react-sdk/pull/8525)). Fixes #22132. Contributed by @luixxiul.
|
||||
* Hide recently_viewed dropdown when other modal opens ([\#8538](https://github.com/matrix-org/matrix-react-sdk/pull/8538)). Contributed by @yaya-usman.
|
||||
* Only jump to date after pressing the 'go' button ([\#8548](https://github.com/matrix-org/matrix-react-sdk/pull/8548)). Fixes #20799.
|
||||
* Fix download button not working on events that were decrypted too late ([\#8556](https://github.com/matrix-org/matrix-react-sdk/pull/8556)). Fixes #19427.
|
||||
* Align thread summary button with bubble messages on the left side ([\#8388](https://github.com/matrix-org/matrix-react-sdk/pull/8388)). Fixes #21873. Contributed by @luixxiul.
|
||||
* Fix unresponsive notification toggles ([\#8549](https://github.com/matrix-org/matrix-react-sdk/pull/8549)). Fixes #22109.
|
||||
* Set color-scheme property in themes ([\#8547](https://github.com/matrix-org/matrix-react-sdk/pull/8547)). Fixes #22124.
|
||||
* Improve the styling of error messages during search initialization. ([\#6899](https://github.com/matrix-org/matrix-react-sdk/pull/6899)). Fixes #19245 and #18164. Contributed by @KalleStruik.
|
||||
* Don't leave button tooltips open when closing modals ([\#8546](https://github.com/matrix-org/matrix-react-sdk/pull/8546)). Fixes #22121.
|
||||
* update matrix-analytics-events ([\#8543](https://github.com/matrix-org/matrix-react-sdk/pull/8543)).
|
||||
* Handle Jitsi Meet crashes more gracefully ([\#8541](https://github.com/matrix-org/matrix-react-sdk/pull/8541)).
|
||||
* Fix regression around pasting links ([\#8537](https://github.com/matrix-org/matrix-react-sdk/pull/8537)). Fixes #22117.
|
||||
* Fixes suggested room not ellipsized on shrinking ([\#8536](https://github.com/matrix-org/matrix-react-sdk/pull/8536)). Contributed by @yaya-usman.
|
||||
* Add global spacing between display name and location body ([\#8523](https://github.com/matrix-org/matrix-react-sdk/pull/8523)). Fixes #22111. Contributed by @luixxiul.
|
||||
* Add box-shadow to the reply preview on the main (left) panel only ([\#8397](https://github.com/matrix-org/matrix-react-sdk/pull/8397)). Fixes #21894. Contributed by @luixxiul.
|
||||
* Set line-height: 1 to RedactedBody inside GenericEventListSummary for IRC/modern layout ([\#8529](https://github.com/matrix-org/matrix-react-sdk/pull/8529)). Fixes #22112. Contributed by @luixxiul.
|
||||
* Fix position of timestamp on the chat panel in IRC layout and message edits history modal window ([\#8464](https://github.com/matrix-org/matrix-react-sdk/pull/8464)). Fixes #22011 and #22014. Contributed by @luixxiul.
|
||||
* Fix unexpected and inconsistent inheritance of line-height property for mx_TextualEvent ([\#8485](https://github.com/matrix-org/matrix-react-sdk/pull/8485)). Fixes #22041. Contributed by @luixxiul.
|
||||
* Set the same margin to the right side of NewRoomIntro on TimelineCard ([\#8453](https://github.com/matrix-org/matrix-react-sdk/pull/8453)). Contributed by @luixxiul.
|
||||
* Remove duplicate tooltip from user pills ([\#8512](https://github.com/matrix-org/matrix-react-sdk/pull/8512)).
|
||||
* Set max-width for MLocationBody and MLocationBody_map by default ([\#8519](https://github.com/matrix-org/matrix-react-sdk/pull/8519)). Fixes #21983. Contributed by @luixxiul.
|
||||
* Simplify ReplyPreview UI implementation ([\#8516](https://github.com/matrix-org/matrix-react-sdk/pull/8516)). Fixes #22091. Contributed by @luixxiul.
|
||||
* Fix thread summary overflow on narrow message panel on bubble message layout ([\#8520](https://github.com/matrix-org/matrix-react-sdk/pull/8520)). Fixes #22097. Contributed by @luixxiul.
|
||||
* Live location sharing - refresh beacon timers on tab becoming active ([\#8515](https://github.com/matrix-org/matrix-react-sdk/pull/8515)).
|
||||
* Enlarge emoji again ([\#8509](https://github.com/matrix-org/matrix-react-sdk/pull/8509)). Fixes #22086.
|
||||
* Order receipts with the most recent on the right ([\#8506](https://github.com/matrix-org/matrix-react-sdk/pull/8506)). Fixes #22044.
|
||||
* Disconnect from video rooms when leaving ([\#8500](https://github.com/matrix-org/matrix-react-sdk/pull/8500)).
|
||||
* Fix soft crash around threads when room isn't yet in store ([\#8496](https://github.com/matrix-org/matrix-react-sdk/pull/8496)). Fixes #22047.
|
||||
* Fix reading of cached room device setting values ([\#8491](https://github.com/matrix-org/matrix-react-sdk/pull/8491)).
|
||||
* Add loading spinners to threads panels ([\#8490](https://github.com/matrix-org/matrix-react-sdk/pull/8490)). Fixes #21335.
|
||||
* Fix forwarding UI papercuts ([\#8482](https://github.com/matrix-org/matrix-react-sdk/pull/8482)). Fixes #17616.
|
||||
|
||||
Changes in [1.10.12](https://github.com/vector-im/element-web/releases/tag/v1.10.12) (2022-05-10)
|
||||
=================================================================================================
|
||||
|
||||
|
||||
18
README.md
18
README.md
@@ -1,3 +1,12 @@
|
||||
[](https://matrix.to/#/#element-web:matrix.org)
|
||||

|
||||

|
||||
[](https://translate.element.io/engage/element-web/)
|
||||
[](https://sonarcloud.io/summary/new_code?id=element-web)
|
||||
[](https://sonarcloud.io/summary/new_code?id=element-web)
|
||||
[](https://sonarcloud.io/summary/new_code?id=element-web)
|
||||
[](https://sonarcloud.io/summary/new_code?id=element-web)
|
||||
|
||||
Element
|
||||
=======
|
||||
|
||||
@@ -250,10 +259,11 @@ Before attempting to develop on Element you **must** read the [developer guide
|
||||
for `matrix-react-sdk`](https://github.com/matrix-org/matrix-react-sdk#developer-guide), which
|
||||
also defines the design, architecture and style for Element too.
|
||||
|
||||
Before starting work on a feature, it's best to ensure your plan aligns well
|
||||
with our vision for Element. Please chat with the team in
|
||||
[#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) before you
|
||||
start so we can ensure it's something we'd be willing to merge.
|
||||
Read the [Choosing an issue](docs/choosing-an-issue.md) page for some guidance
|
||||
about where to start. Before starting work on a feature, it's best to ensure
|
||||
your plan aligns well with our vision for Element. Please chat with the team in
|
||||
[#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) before
|
||||
you start so we can ensure it's something we'd be willing to merge.
|
||||
|
||||
You should also familiarise yourself with the ["Here be Dragons" guide
|
||||
](https://docs.google.com/document/d/12jYzvkidrp1h7liEuLIe6BMdU0NUjndUYI971O06ooM)
|
||||
|
||||
81
docs/choosing-an-issue.md
Normal file
81
docs/choosing-an-issue.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Choosing an issue to work on
|
||||
|
||||
So you want to contribute to Element Web? That is awesome!
|
||||
|
||||
If you're not sure where to start, make sure you read
|
||||
[CONTRIBUTING.md](../CONTRIBUTING.md), and the
|
||||
[Development](../README.md#development) and
|
||||
[Setting up a dev environment](../README.md#setting-up-a-dev-environment)
|
||||
sections of the README.
|
||||
|
||||
Maybe you've got something specific you'd like to work on? If so, make sure you
|
||||
create an issue and
|
||||
[discuss it with the developers](https://matrix.to/#/#element-dev:matrix.org)
|
||||
before you put a lot of time into it.
|
||||
|
||||
If you're looking for inspiration on where to start, keep reading!
|
||||
|
||||
## Finding a good first issue
|
||||
|
||||
All the issues for Element Web live in the
|
||||
[element-web](https://github.com/vector-im/element-web) repository, including
|
||||
issues that actually need fixing in `matrix-react-sdk` or one of the related
|
||||
repos.
|
||||
|
||||
The first place to look is for
|
||||
[issues tagged with "good first issue"](https://github.com/vector-im/element-web/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
|
||||
|
||||
Look through that list and find something that catches your interest. If there
|
||||
is nothing, there, try gently asking in
|
||||
[#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) for
|
||||
someone to add something.
|
||||
|
||||
When you're looking through the list, here are some things that might make an
|
||||
issue a **GOOD** choice:
|
||||
|
||||
* It is a problem or feature you care about.
|
||||
* It concerns a type of code you know a little about.
|
||||
* You think you can understand what's needed.
|
||||
* It already has approval from Element Web's designers (look for comments from
|
||||
members of the
|
||||
[Product](https://github.com/orgs/vector-im/teams/product/members) or
|
||||
[Design](https://github.com/orgs/vector-im/teams/design/members) teams).
|
||||
|
||||
Here are some things that might make it a **BAD** choice:
|
||||
|
||||
* You don't understand it (maybe add a comment asking a clarifying question).
|
||||
* It sounds difficult, or is part of a larger change you don't know about.
|
||||
* **It is tagged with `X-Needs-Design` or `X-Needs-Product`.**
|
||||
|
||||
**Element Web's Design and Product teams tend to be very busy**, so if you make
|
||||
changes that require approval from one of those teams, you will probably have
|
||||
to wait a very long time. The kind of change affected by this is changing the
|
||||
way the product works, or how it looks in a specific area.
|
||||
|
||||
## Finding a good second issue
|
||||
|
||||
Once you've fixed a few small things, you can consider taking on something a
|
||||
little larger. This should mostly be driven by what you find interesting, but
|
||||
you may also find the
|
||||
[Help Wanted](https://github.com/vector-im/element-web/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22Help+Wanted%22)
|
||||
label useful.
|
||||
|
||||
Note that the same comment applies as in the previous section: if you want to
|
||||
work in areas that require Design or Product approval, you should look to join
|
||||
existing work that is already designed, as getting approval for your random
|
||||
change will take a very long time.
|
||||
|
||||
So you should **always avoid issues tagged with `X-Needs-Design` or
|
||||
`X-Needs-Product`**.
|
||||
|
||||
## Asking questions
|
||||
|
||||
Feel free to ask questions about the issues or how to choose them in the
|
||||
[#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) Matrix
|
||||
room.
|
||||
|
||||
## Thank you
|
||||
|
||||
Thank you again for contributing to Element Web. We welcome your contributions
|
||||
and are grateful for your work. We find working on it great fun, and we hope
|
||||
you do too!
|
||||
@@ -14,7 +14,7 @@ for the desktop app the application will need to be exited fully (including via
|
||||
## Homeserver configuration
|
||||
|
||||
In order for Element to even start you will need to tell it what homeserver to connect to *by default*. Users will be
|
||||
able to use a different homeserver if they like, though this can be disabled with `"disable_custom_urls": false` in your
|
||||
able to use a different homeserver if they like, though this can be disabled with `"disable_custom_urls": true` in your
|
||||
config.
|
||||
|
||||
One of the following options **must** be supplied:
|
||||
@@ -95,7 +95,8 @@ instance. As of writing those settings are not fully documented, however a few a
|
||||
}
|
||||
}
|
||||
```
|
||||
These values will take priority over the hardcoded defaults for the settings.
|
||||
These values will take priority over the hardcoded defaults for the settings. For a list of available settings, see
|
||||
[Settings.tsx](https://github.com/matrix-org/matrix-react-sdk/blob/develop/src/settings/Settings.tsx).
|
||||
|
||||
## Customisation & branding
|
||||
|
||||
@@ -192,7 +193,7 @@ Starting with `branding`, the following subproperties are available:
|
||||
`welcome.html` that ships with Element will be used instead.
|
||||
2. `home_url`: A URL to an HTML page to show within the app as the "home" page. When the app doesn't have a room/screen to
|
||||
show the user, it will use the home page instead. The home page is additionally accessible from the user menu. By default,
|
||||
no home page is set and therefore a hardcoded landing screen is used.
|
||||
no home page is set and therefore a hardcoded landing screen is used. More documentation and examples are [here](./custom-home.md).
|
||||
3. `login_for_welcome`: When `true` (default `false`), the app will use the login form as a welcome page instead of the welcome
|
||||
page itself. This disables use of `welcome_url` and all welcome page functionality.
|
||||
|
||||
|
||||
65
docs/custom-home.md
Normal file
65
docs/custom-home.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# Custom Home Page
|
||||
|
||||
The home page is shown whenever the user is logged in, but no room is selected.
|
||||
A custom `home.html` replacing the default home page can be configured either in `.well-known/matrix/client` or `config.json`.
|
||||
Such a custom home page can be used to communicate helpful information and important rules to the users.
|
||||
|
||||
## Configuration
|
||||
|
||||
To provide a custom home page for all element-web/desktop users of a homeserver, include the following in `.well-known/matrix/client`:
|
||||
|
||||
```
|
||||
{
|
||||
"io.element.embedded_pages": {
|
||||
"home_url": "https://example.org/home.html"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The home page can be overridden in `config.json` to provide all users of an element-web installation with the same experience:
|
||||
|
||||
```
|
||||
{
|
||||
"embeddedPages": {
|
||||
"homeUrl": "https://example.org/home.html"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## `home.html` Example
|
||||
|
||||
The following is a simple example for a custom `home.html`:
|
||||
|
||||
```
|
||||
<style type="text/css">
|
||||
.tos {
|
||||
width: auto;
|
||||
color: black;
|
||||
background : #ffcccb;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
|
||||
<h1>The example.org Matrix Server</h1>
|
||||
|
||||
<div class="tos">
|
||||
<p>Behave appropriately.</p>
|
||||
</div>
|
||||
|
||||
<h2>Start Chatting</h2>
|
||||
<ul>
|
||||
<li><a href="#/dm">Send a Direct Message</a></li>
|
||||
<li><a href="#/directory">Explore Public Rooms</a></li>
|
||||
<li><a href="#/new">Create a Group Chat</a></li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
When choosing colors, be aware that the home page may be displayed in either light or dark mode.
|
||||
|
||||
It may be needed to set CORS headers for the `home.html` to enable element-desktop to fetch it, with e.g., the following nginx config:
|
||||
|
||||
```
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
```
|
||||
|
||||
19
docs/labs.md
19
docs/labs.md
@@ -34,11 +34,6 @@ date from the calendar.
|
||||
|
||||
Also adds the `/jumptodate 2022-01-31` slash command.
|
||||
|
||||
## Custom status (`feature_custom_status`)
|
||||
|
||||
An experimental approach for supporting custom status messages across DMs. To set a status, click on
|
||||
your avatar next to the message composer.
|
||||
|
||||
## Render simple counters in room header (`feature_state_counters`)
|
||||
|
||||
Allows rendering of labelled counters above the message list.
|
||||
@@ -62,10 +57,6 @@ Once enabled, send a custom state event to a room to set values:
|
||||
|
||||
That's it. Now should see your new counter under the header.
|
||||
|
||||
## Multiple integration managers (`feature_many_integration_managers`)
|
||||
|
||||
Exposes a way to access all the integration managers known to Element. This is an implementation of [MSC1957](https://github.com/matrix-org/matrix-doc/pull/1957).
|
||||
|
||||
## New ways to ignore people (`feature_mjolnir`)
|
||||
|
||||
When enabled, a new settings tab appears for users to be able to manage their ban lists.
|
||||
@@ -108,19 +99,15 @@ For some sample themes, check out [aaronraimist/element-themes](https://github.c
|
||||
|
||||
## Message preview tweaks
|
||||
|
||||
To enable message previews for reactions in all rooms, enable `feature_roomlist_preview_reactions_all`.
|
||||
To enable message previews for reactions in DMs, enable `feature_roomlist_preview_reactions_dms`, ignored when it is enabled for all rooms.
|
||||
To enable message previews in the left panel for reactions in all rooms, enable `feature_roomlist_preview_reactions_all`.
|
||||
|
||||
To enable message previews for reactions in DMs only, enable `feature_roomlist_preview_reactions_dms`. This is ignored when it is enabled for all rooms.
|
||||
|
||||
## Dehydrated devices (`feature_dehydration`)
|
||||
|
||||
Allows users to receive encrypted messages by creating a device that is stored
|
||||
encrypted on the server, as described in [MSC2697](https://github.com/matrix-org/matrix-doc/pull/2697).
|
||||
|
||||
## Do not disturb (`feature_dnd`)
|
||||
|
||||
Enables UI for turning on "do not disturb" mode for the current device. When DND mode is engaged, popups
|
||||
and notification noises are suppressed. Not perfect, but can help reduce noise.
|
||||
|
||||
## Hidden read receipts (`feature_hidden_read_receipts`)
|
||||
|
||||
Enables sending hidden read receipts as per [MSC2285](https://github.com/matrix-org/matrix-doc/pull/2285)
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
2. After registering check if you got an email to verify your account and click the link (if there is none head to step 1.4)
|
||||
3. Log into weblate
|
||||
4. Head to https://translate.element.io/accounts/profile/ and select the languages you know and maybe another language you know too.
|
||||
6. Head to https://translate.element.io/accounts/profile/#subscriptions and select Element Web as Project
|
||||
|
||||
## How to check if your language already is being translated
|
||||
|
||||
|
||||
23
package.json
23
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.10.12",
|
||||
"version": "1.10.13",
|
||||
"description": "A feature-rich client for Matrix.org",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -49,7 +49,8 @@
|
||||
"lint:js-fix": "eslint --fix src",
|
||||
"lint:types": "tsc --noEmit --jsx react",
|
||||
"lint:style": "stylelint \"res/css/**/*.scss\"",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"coverage": "yarn test --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz",
|
||||
@@ -57,8 +58,8 @@
|
||||
"gfm.css": "^1.1.2",
|
||||
"jsrsasign": "^10.2.0",
|
||||
"katex": "^0.12.0",
|
||||
"matrix-js-sdk": "17.2.0",
|
||||
"matrix-react-sdk": "3.44.0",
|
||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
|
||||
"matrix-react-sdk": "github:matrix-org/matrix-react-sdk#develop",
|
||||
"matrix-widget-api": "^0.1.0-beta.18",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "17.0.2",
|
||||
@@ -119,9 +120,10 @@
|
||||
"jest": "^26.6.3",
|
||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||
"jest-raw-loader": "^1.0.1",
|
||||
"jest-sonar-reporter": "^2.0.0",
|
||||
"json-loader": "^0.5.7",
|
||||
"loader-utils": "^1.4.0",
|
||||
"matrix-mock-request": "^1.2.3",
|
||||
"matrix-mock-request": "^2.0.0",
|
||||
"matrix-react-test-utils": "^0.2.3",
|
||||
"matrix-web-i18n": "^1.2.0",
|
||||
"mini-css-extract-plugin": "^0.12.0",
|
||||
@@ -190,6 +192,15 @@
|
||||
"transformIgnorePatterns": [
|
||||
"/node_modules/(?!matrix-js-sdk).+$",
|
||||
"/node_modules/(?!matrix-react-sdk).+$"
|
||||
]
|
||||
],
|
||||
"coverageReporters": [
|
||||
"text-summary",
|
||||
"lcov"
|
||||
],
|
||||
"testResultsProcessor": "jest-sonar-reporter"
|
||||
},
|
||||
"jestSonar": {
|
||||
"reportPath": "coverage",
|
||||
"sonar56x": true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
./node_modules/matrix-js-sdk/release.sh -n -z "$orig_args"
|
||||
./node_modules/matrix-js-sdk/release.sh -n "$orig_args"
|
||||
|
||||
release="${1#v}"
|
||||
tag="v${release}"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
* ```
|
||||
*/
|
||||
|
||||
|
||||
class Optional {
|
||||
static from(value) {
|
||||
return value && Some.of(value) || None;
|
||||
|
||||
@@ -102,7 +102,7 @@ function fetchAsSubject(endpoint) {
|
||||
const contentLength = res.headers.get("content-length");
|
||||
const context = contentLength ? { length: parseInt(contentLength) } : {};
|
||||
|
||||
const streamer = observeReadableStream(res.body, context, endpoint);
|
||||
const streamer = observeReadableStream(res.body, context);
|
||||
streamer.subscribe((value) => {
|
||||
fetcher.next(value);
|
||||
});
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Rageshake decoder ring</title>
|
||||
<script crossorigin src="https://unpkg.com/source-map@0.7.3/dist/source-map.js"></script>
|
||||
|
||||
@@ -47,7 +47,6 @@ h1::after {
|
||||
display: flex;
|
||||
-webkit-justify-content: space-around;
|
||||
-ms-flex-pack: distribute;
|
||||
justify-content: space-around;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
|
||||
@@ -35,6 +35,7 @@ const INCLUDE_LANGS = [
|
||||
{'value': 'ja', 'label': '日本語'},
|
||||
{'value': 'kab', 'label': 'Taqbaylit'},
|
||||
{'value': 'ko', 'label': '한국어'},
|
||||
{'value': 'lo', 'label': 'ລາວ'},
|
||||
{'value': 'lt', 'label': 'Lietuvių'},
|
||||
{'value': 'lv', 'label': 'Latviešu'},
|
||||
{'value': 'nb_NO', 'label': 'Norwegian Bokmål'},
|
||||
@@ -232,8 +233,14 @@ function weblateToCounterpart(inTrs) {
|
||||
if (keyParts.length === 2) {
|
||||
let obj = outTrs[keyParts[0]];
|
||||
if (obj === undefined) {
|
||||
obj = {};
|
||||
outTrs[keyParts[0]] = obj;
|
||||
obj = outTrs[keyParts[0]] = {};
|
||||
} else if (typeof obj === "string") {
|
||||
// This is a transitional edge case if a string went from singular to pluralised and both still remain
|
||||
// in the translation json file. Use the singular translation as `other` and merge pluralisation atop.
|
||||
obj = outTrs[keyParts[0]] = {
|
||||
"other": inTrs[key],
|
||||
};
|
||||
console.warn("Found entry in i18n file in both singular and pluralised form", keyParts[0]);
|
||||
}
|
||||
obj[keyParts[1]] = inTrs[key];
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/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.
|
||||
|
||||
@@ -23,9 +23,11 @@ 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))
|
||||
@@ -57,10 +59,11 @@ def move_bundles(source, dest):
|
||||
else:
|
||||
renames[os.path.join(source, f)] = dst
|
||||
|
||||
for (src, dst) in renames.iteritems():
|
||||
for (src, dst) in renames.items():
|
||||
print ("Move %s -> %s" % (src, dst))
|
||||
os.rename(src, dst)
|
||||
|
||||
|
||||
class Deployer:
|
||||
def __init__(self):
|
||||
self.packages_path = "."
|
||||
@@ -100,7 +103,7 @@ class Deployer:
|
||||
print ("Extracted into: %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)
|
||||
@@ -139,6 +142,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(
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Fetches the js-sdk and matrix-react-sdk dependencies for development
|
||||
# or testing purposes
|
||||
# If there exists a branch of that dependency with the same name as
|
||||
# the branch the current checkout is on, use that branch. Otherwise,
|
||||
# use develop.
|
||||
|
||||
set -ex
|
||||
|
||||
GIT_CLONE_ARGS=("$@")
|
||||
[ -z "$defbranch" ] && defbranch="develop"
|
||||
|
||||
# clone a specific branch of a github repo
|
||||
function clone() {
|
||||
org=$1
|
||||
repo=$2
|
||||
branch=$3
|
||||
|
||||
# Chop 'origin' off the start as jenkins ends up using
|
||||
# branches on the origin, but this doesn't work if we
|
||||
# specify the branch when cloning.
|
||||
branch=${branch#origin/}
|
||||
|
||||
if [ -n "$branch" ]
|
||||
then
|
||||
echo "Trying to use $org/$repo#$branch"
|
||||
# Disable auth prompts: https://serverfault.com/a/665959
|
||||
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch $branch \
|
||||
"${GIT_CLONE_ARGS[@]}"
|
||||
return $?
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
function dodep() {
|
||||
deforg=$1
|
||||
defrepo=$2
|
||||
rm -rf $defrepo
|
||||
|
||||
# Try the PR author's branch in case it exists on the deps as well.
|
||||
# Try the target branch of the push or PR.
|
||||
# Use the default branch as the last resort.
|
||||
if [[ "$BUILDKITE" == true ]]; then
|
||||
# If BUILDKITE_BRANCH is set, it will contain either:
|
||||
# * "branch" when the author's branch and target branch are in the same repo
|
||||
# * "author:branch" when the author's branch is in their fork
|
||||
# We can split on `:` into an array to check.
|
||||
BUILDKITE_BRANCH_ARRAY=(${BUILDKITE_BRANCH//:/ })
|
||||
if [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "2" ]]; then
|
||||
prAuthor=${BUILDKITE_BRANCH_ARRAY[0]}
|
||||
prBranch=${BUILDKITE_BRANCH_ARRAY[1]}
|
||||
else
|
||||
prAuthor=$deforg
|
||||
prBranch=$BUILDKITE_BRANCH
|
||||
fi
|
||||
clone $prAuthor $defrepo $prBranch ||
|
||||
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH ||
|
||||
clone $deforg $defrepo $defbranch ||
|
||||
return $?
|
||||
else
|
||||
clone $deforg $defrepo $ghprbSourceBranch ||
|
||||
clone $deforg $defrepo $GIT_BRANCH ||
|
||||
clone $deforg $defrepo `git rev-parse --abbrev-ref HEAD` ||
|
||||
clone $deforg $defrepo $defbranch ||
|
||||
return $?
|
||||
fi
|
||||
|
||||
echo "$defrepo set to branch "`git -C "$defrepo" rev-parse --abbrev-ref HEAD`
|
||||
}
|
||||
|
||||
##############################
|
||||
|
||||
echo 'Setting up matrix-js-sdk'
|
||||
|
||||
dodep matrix-org matrix-js-sdk
|
||||
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
yarn link matrix-js-sdk
|
||||
|
||||
##############################
|
||||
|
||||
echo 'Setting up matrix-react-sdk'
|
||||
|
||||
dodep matrix-org matrix-react-sdk
|
||||
|
||||
pushd matrix-react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
yarn link matrix-react-sdk
|
||||
|
||||
##############################
|
||||
78
scripts/fetchdep.sh
Executable file
78
scripts/fetchdep.sh
Executable file
@@ -0,0 +1,78 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
deforg="$1"
|
||||
defrepo="$2"
|
||||
defbranch="$3"
|
||||
|
||||
[ -z "$defbranch" ] && defbranch="develop"
|
||||
|
||||
rm -r "$defrepo" || true
|
||||
|
||||
# A function that clones a branch of a repo based on the org, repo and branch
|
||||
clone() {
|
||||
org=$1
|
||||
repo=$2
|
||||
branch=$3
|
||||
if [ -n "$branch" ]
|
||||
then
|
||||
echo "Trying to use $org/$repo#$branch"
|
||||
# Disable auth prompts: https://serverfault.com/a/665959
|
||||
GIT_TERMINAL_PROMPT=0 git clone https://github.com/$org/$repo.git $repo --branch "$branch" --depth 1 && exit 0
|
||||
fi
|
||||
}
|
||||
|
||||
# A function that gets info about a PR from the GitHub API based on its number
|
||||
getPRInfo() {
|
||||
number=$1
|
||||
if [ -n "$number" ]; then
|
||||
echo "Getting info about a PR with number $number"
|
||||
|
||||
apiEndpoint="https://api.github.com/repos/${REPOSITORY:-"vector-im/element-web"}/pulls/"
|
||||
apiEndpoint+=$number
|
||||
|
||||
head=$(curl $apiEndpoint | jq -r '.head.label')
|
||||
fi
|
||||
}
|
||||
|
||||
# Some CIs don't give us enough info, so we just get the PR number and ask the
|
||||
# GH API for more info - "fork:branch". Some give us this directly.
|
||||
if [ -n "$BUILDKITE_BRANCH" ]; then
|
||||
# BuildKite
|
||||
head=$BUILDKITE_BRANCH
|
||||
elif [ -n "$PR_NUMBER" ]; then
|
||||
# GitHub
|
||||
getPRInfo $PR_NUMBER
|
||||
elif [ -n "$REVIEW_ID" ]; then
|
||||
# Netlify
|
||||
getPRInfo $REVIEW_ID
|
||||
fi
|
||||
|
||||
# for forks, $head will be in the format "fork:branch", so we split it by ":"
|
||||
# into an array. On non-forks, this has the effect of splitting into a single
|
||||
# element array given ":" shouldn't appear in the head - it'll just be the
|
||||
# branch name. Based on the results, we clone.
|
||||
BRANCH_ARRAY=(${head//:/ })
|
||||
TRY_ORG=$deforg
|
||||
TRY_BRANCH=${BRANCH_ARRAY[0]}
|
||||
if [[ "$head" == *":"* ]]; then
|
||||
# ... but only match that fork if it's a real fork
|
||||
if [ "${BRANCH_ARRAY[0]}" != "matrix-org" ]; then
|
||||
TRY_ORG=${BRANCH_ARRAY[0]}
|
||||
fi
|
||||
TRY_BRANCH=${BRANCH_ARRAY[1]}
|
||||
fi
|
||||
clone ${TRY_ORG} $defrepo ${TRY_BRANCH}
|
||||
|
||||
# Try the target branch of the push or PR.
|
||||
if [ -n "$GITHUB_BASE_REF" ]; then
|
||||
clone $deforg $defrepo $GITHUB_BASE_REF
|
||||
elif [ -n "$BUILDKITE_PULL_REQUEST_BASE_BRANCH" ]; then
|
||||
clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH
|
||||
fi
|
||||
|
||||
# Try HEAD which is the branch name in Netlify (not BRANCH which is pull/xxxx/head for PR builds)
|
||||
clone $deforg $defrepo $HEAD
|
||||
# Use the default branch as the last resort.
|
||||
clone $deforg $defrepo $defbranch
|
||||
@@ -4,7 +4,7 @@
|
||||
# these dependencies are git checkouts.
|
||||
|
||||
# Since the deps are fetched from git, we can rev-parse
|
||||
REACT_SHA=$(cd node_modules/matrix-react-sdk; git rev-parse --short=12 HEAD)
|
||||
JSSDK_SHA=$(cd node_modules/matrix-js-sdk; git rev-parse --short=12 HEAD)
|
||||
REACT_SHA=$(git -C node_modules/matrix-react-sdk rev-parse --short=12 HEAD)
|
||||
JSSDK_SHA=$(git -C node_modules/matrix-js-sdk rev-parse --short=12 HEAD)
|
||||
VECTOR_SHA=$(git rev-parse --short=12 HEAD) # use the ACTUAL SHA rather than assume develop
|
||||
echo $VECTOR_SHA-react-$REACT_SHA-js-$JSSDK_SHA
|
||||
|
||||
49
scripts/layered.sh
Executable file
49
scripts/layered.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
# Creates a layered environment with the full repo for the app and SDKs cloned
|
||||
# and linked. This gives an element-web dev environment ready to build with
|
||||
# matching branches of react-sdk's dependencies so that changes can be tested
|
||||
# in element-web.
|
||||
|
||||
# Note that this style is different from the recommended developer setup: this
|
||||
# file nests js-sdk and matrix-react-sdk inside element-web, while the local
|
||||
# development setup places them all at the same level. We are nesting them here
|
||||
# because some CI systems do not allow moving to a directory above the checkout
|
||||
# for the primary repo (element-web in this case).
|
||||
|
||||
# Install dependencies, as we'll be using fetchdep.sh from matrix-react-sdk
|
||||
yarn install --pure-lockfile
|
||||
|
||||
# Pass appropriate repo to fetchdep.sh
|
||||
export PR_ORG=vector-im
|
||||
export PR_REPO=element-web
|
||||
|
||||
# Set up the js-sdk first
|
||||
node_modules/matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-js-sdk
|
||||
pushd matrix-js-sdk
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
# Also set up matrix-analytics-events so we get the latest from
|
||||
# the main branch or a branch with matching name
|
||||
node_modules/matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-analytics-events main
|
||||
pushd matrix-analytics-events
|
||||
yarn link
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
# Now set up the react-sdk
|
||||
node_modules/matrix-react-sdk/scripts/fetchdep.sh matrix-org matrix-react-sdk
|
||||
pushd matrix-react-sdk
|
||||
yarn link
|
||||
yarn link matrix-js-sdk
|
||||
yarn link matrix-analytics-events
|
||||
yarn install --pure-lockfile
|
||||
popd
|
||||
|
||||
# Link the layers into element-web
|
||||
yarn link matrix-js-sdk
|
||||
yarn link matrix-react-sdk
|
||||
@@ -1,45 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
|
||||
# auto-deploy script for https://develop.element.io
|
||||
#
|
||||
# Listens for buildkite webhook pokes (https://buildkite.com/docs/apis/webhooks)
|
||||
# When it gets one, downloads the artifact from buildkite
|
||||
# and deploys it as the new version.
|
||||
#
|
||||
|
||||
# Listens for Github Action webhook pokes (https://github.com/marketplace/actions/workflow-webhook-action)
|
||||
# When it gets one: downloads the artifact from github actions and deploys it as the new version.
|
||||
|
||||
# Requires the following python packages:
|
||||
#
|
||||
# - requests
|
||||
# - flask
|
||||
#
|
||||
# - python-github-webhook
|
||||
|
||||
from __future__ import print_function
|
||||
import json, requests, tarfile, argparse, os, errno
|
||||
import argparse
|
||||
import os
|
||||
import errno
|
||||
import time
|
||||
import traceback
|
||||
from urlparse import urljoin
|
||||
import glob
|
||||
import re
|
||||
import shutil
|
||||
import threading
|
||||
from Queue import Queue
|
||||
|
||||
from flask import Flask, jsonify, request, abort
|
||||
import glob
|
||||
from io import BytesIO
|
||||
from urllib.request import urlopen
|
||||
from zipfile import ZipFile
|
||||
|
||||
from github_webhook import Webhook
|
||||
from flask import Flask, abort
|
||||
|
||||
from deploy import Deployer, DeployException
|
||||
|
||||
app = Flask(__name__)
|
||||
webhook = Webhook(app, endpoint="/")
|
||||
|
||||
deployer = None
|
||||
arg_extract_path = None
|
||||
arg_symlink = None
|
||||
arg_webhook_token = None
|
||||
arg_api_token = None
|
||||
|
||||
workQueue = Queue()
|
||||
|
||||
def create_symlink(source, linkname):
|
||||
def create_symlink(source: str, linkname: str):
|
||||
try:
|
||||
os.symlink(source, linkname)
|
||||
except OSError, e:
|
||||
except OSError as e:
|
||||
if e.errno == errno.EEXIST:
|
||||
# atomic modification
|
||||
os.symlink(source, linkname + ".tmp")
|
||||
@@ -47,118 +42,43 @@ def create_symlink(source, linkname):
|
||||
else:
|
||||
raise e
|
||||
|
||||
def req_headers():
|
||||
return {
|
||||
"Authorization": "Bearer %s" % (arg_api_token,),
|
||||
}
|
||||
|
||||
# Buildkite considers a poke to have failed if it has to wait more than 10s for
|
||||
# data (any data, not just the initial response) and it normally takes longer than
|
||||
# that to download an artifact from buildkite. Apparently there is no way in flask
|
||||
# to finish the response and then keep doing stuff, so instead this has to involve
|
||||
# threading. Sigh.
|
||||
def worker_thread():
|
||||
while True:
|
||||
toDeploy = workQueue.get()
|
||||
deploy_buildkite_artifact(*toDeploy)
|
||||
|
||||
@app.route("/", methods=["POST"])
|
||||
def on_receive_buildkite_poke():
|
||||
got_webhook_token = request.headers.get('X-Buildkite-Token')
|
||||
if got_webhook_token != arg_webbook_token:
|
||||
print("Denying request with incorrect webhook token: %s" % (got_webhook_token,))
|
||||
abort(400, "Incorrect webhook token")
|
||||
@webhook.hook(event_type="workflow_run")
|
||||
def on_deployment(payload: dict):
|
||||
repository = payload.get("repository")
|
||||
if repository is None:
|
||||
abort(400, "No 'repository' specified")
|
||||
return
|
||||
|
||||
required_api_prefix = None
|
||||
if arg_buildkite_org is not None:
|
||||
required_api_prefix = 'https://api.buildkite.com/v2/organizations/%s' % (arg_buildkite_org,)
|
||||
|
||||
incoming_json = request.get_json()
|
||||
if not incoming_json:
|
||||
abort(400, "No JSON provided!")
|
||||
return
|
||||
print("Incoming JSON: %s" % (incoming_json,))
|
||||
|
||||
event = incoming_json.get("event")
|
||||
if event is None:
|
||||
abort(400, "No 'event' specified")
|
||||
workflow = payload.get("workflow")
|
||||
if repository is None:
|
||||
abort(400, "No 'workflow' specified")
|
||||
return
|
||||
|
||||
if event == 'ping':
|
||||
print("Got ping request - responding")
|
||||
return jsonify({'response': 'pong!'})
|
||||
|
||||
if event != 'build.finished':
|
||||
print("Rejecting '%s' event")
|
||||
abort(400, "Unrecognised event")
|
||||
request_id = payload.get("requestID")
|
||||
if request_id is None:
|
||||
abort(400, "No 'request_id' specified")
|
||||
return
|
||||
|
||||
build_obj = incoming_json.get("build")
|
||||
if build_obj is None:
|
||||
abort(400, "No 'build' object")
|
||||
if arg_github_org is not None and not repository.startswith(arg_github_org):
|
||||
print("Denying poke for repository with incorrect prefix: %s" % (repository,))
|
||||
abort(400, "Invalid repository")
|
||||
return
|
||||
|
||||
build_url = build_obj.get('url')
|
||||
if build_url is None:
|
||||
abort(400, "build has no url")
|
||||
if arg_github_workflow is not None and workflow != arg_github_workflow:
|
||||
print("Denying poke for incorrect workflow: %s" % (workflow,))
|
||||
abort(400, "Incorrect workflow")
|
||||
return
|
||||
|
||||
if required_api_prefix is not None and not build_url.startswith(required_api_prefix):
|
||||
print("Denying poke for build url with incorrect prefix: %s" % (build_url,))
|
||||
abort(400, "Invalid build url")
|
||||
artifact_url = payload.get("data", {}).get("url")
|
||||
if artifact_url is None:
|
||||
abort(400, "No 'data.url' specified")
|
||||
return
|
||||
|
||||
build_num = build_obj.get('number')
|
||||
if build_num is None:
|
||||
abort(400, "build has no number")
|
||||
return
|
||||
deploy_artifact(artifact_url, request_id)
|
||||
|
||||
pipeline_obj = incoming_json.get("pipeline")
|
||||
if pipeline_obj is None:
|
||||
abort(400, "No 'pipeline' object")
|
||||
return
|
||||
|
||||
pipeline_name = pipeline_obj.get('name')
|
||||
if pipeline_name is None:
|
||||
abort(400, "pipeline has no name")
|
||||
return
|
||||
|
||||
artifacts_url = build_url + "/artifacts"
|
||||
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']):
|
||||
artifact_to_deploy = artifact
|
||||
if artifact_to_deploy is None:
|
||||
print("No suitable artifacts found")
|
||||
return jsonify({})
|
||||
|
||||
# double paranoia check: make sure the artifact is on the right org too
|
||||
if required_api_prefix is not None and not artifact_to_deploy['url'].startswith(required_api_prefix):
|
||||
print("Denying poke for build url with incorrect prefix: %s" % (artifact_to_deploy['url'],))
|
||||
abort(400, "Refusing to deploy artifact from URL %s", artifact_to_deploy['url'])
|
||||
return
|
||||
|
||||
# there's no point building up a queue of things to deploy, so if there are any pending jobs,
|
||||
# remove them
|
||||
while not workQueue.empty():
|
||||
try:
|
||||
workQueue.get(False)
|
||||
except:
|
||||
pass
|
||||
workQueue.put([artifact_to_deploy, pipeline_name, build_num])
|
||||
|
||||
return jsonify({})
|
||||
|
||||
def deploy_buildkite_artifact(artifact, pipeline_name, build_num):
|
||||
artifact_response = requests.get(artifact['url'], headers=req_headers())
|
||||
artifact_response.raise_for_status()
|
||||
artifact_obj = artifact_response.json()
|
||||
|
||||
def deploy_artifact(artifact_url: str, request_id: str):
|
||||
# we extract into a directory based on the build number. This avoids the
|
||||
# problem of multiple builds building the same git version and thus having
|
||||
# the same tarball name. That would lead to two potential problems:
|
||||
@@ -166,58 +86,42 @@ def deploy_buildkite_artifact(artifact, pipeline_name, build_num):
|
||||
# a good deploy with a bad one
|
||||
# (b) we'll be overwriting the live deployment, which means people might
|
||||
# see half-written files.
|
||||
build_dir = os.path.join(arg_extract_path, "%s-#%s" % (pipeline_name, build_num))
|
||||
try:
|
||||
extracted_dir = deploy_tarball(artifact_obj, build_dir)
|
||||
except DeployException as e:
|
||||
traceback.print_exc()
|
||||
abort(400, e.message)
|
||||
build_dir = os.path.join(arg_extract_path, "gha-%s" % (request_id,))
|
||||
|
||||
create_symlink(source=extracted_dir, linkname=arg_symlink)
|
||||
|
||||
def deploy_tarball(artifact, build_dir):
|
||||
"""Download a tarball from jenkins and unpack it
|
||||
|
||||
Returns:
|
||||
(str) the path to the unpacked deployment
|
||||
"""
|
||||
if os.path.exists(build_dir):
|
||||
raise DeployException(
|
||||
"Not deploying. We have previously deployed this build."
|
||||
)
|
||||
# We have already deployed this, nop
|
||||
return
|
||||
os.mkdir(build_dir)
|
||||
|
||||
print("Fetching artifact %s -> %s..." % (artifact['download_url'], artifact['filename']))
|
||||
|
||||
# Download the tarball here as buildkite needs auth to do this
|
||||
# we don't pgp-sign buildkite artifacts, relying on HTTPS and buildkite
|
||||
# not being evil. If that's not good enough for you, don't use develop.element.io.
|
||||
resp = requests.get(artifact['download_url'], stream=True, headers=req_headers())
|
||||
resp.raise_for_status()
|
||||
with open(artifact['filename'], 'wb') as ofp:
|
||||
shutil.copyfileobj(resp.raw, ofp)
|
||||
print("...download complete. Deploying...")
|
||||
|
||||
# we rely on the fact that flask only serves one request at a time to
|
||||
# ensure that we do not overwrite a tarball from a concurrent request.
|
||||
|
||||
return deployer.deploy(artifact['filename'], build_dir)
|
||||
try:
|
||||
with urlopen(artifact_url) as f:
|
||||
with ZipFile(BytesIO(f.read()), "r") as z:
|
||||
name = next((x for x in z.namelist() if x.endswith(".tar.gz")))
|
||||
z.extract(name, build_dir)
|
||||
extracted_dir = deployer.deploy(os.path.join(build_dir, name), build_dir)
|
||||
create_symlink(source=extracted_dir, linkname=arg_symlink)
|
||||
except DeployException as e:
|
||||
traceback.print_exc()
|
||||
abort(400, str(e))
|
||||
finally:
|
||||
if deployer.should_clean:
|
||||
os.remove(os.path.join(build_dir, name))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser("Runs a Vector redeployment server.")
|
||||
parser = argparse.ArgumentParser("Runs an Element redeployment server.")
|
||||
parser.add_argument(
|
||||
"-p", "--port", dest="port", default=4000, type=int, help=(
|
||||
"The port to listen on for requests from Jenkins."
|
||||
"The port to listen on for redeployment requests."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e", "--extract", dest="extract", default="./extracted", help=(
|
||||
"-e", "--extract", dest="extract", default="./extracted", type=str, help=(
|
||||
"The location to extract .tar.gz files to."
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b", "--bundles-dir", dest="bundles_dir", help=(
|
||||
"-b", "--bundles-dir", dest="bundles_dir", type=str, 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'."
|
||||
@@ -229,7 +133,7 @@ if __name__ == "__main__":
|
||||
)
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s", "--symlink", dest="symlink", default="./latest", help=(
|
||||
"-s", "--symlink", dest="symlink", default="./latest", type=str, 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 /vector directory INSIDE the tarball."
|
||||
@@ -238,71 +142,65 @@ if __name__ == "__main__":
|
||||
|
||||
# --include ../../config.json ./localhost.json homepages/*
|
||||
parser.add_argument(
|
||||
"--include", nargs='*', default='./config*.json', help=(
|
||||
"--include", nargs='*', default='./config*.json', type=str, 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(
|
||||
"--test", dest="tarball_uri", help=(
|
||||
"Don't start an HTTP listener. Instead download a build from Jenkins \
|
||||
immediately."
|
||||
"--test", dest="tarball_uri", type=str, help=(
|
||||
"Don't start an HTTP listener. Instead download a build from this URL immediately."
|
||||
),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--webhook-token", dest="webhook_token", help=(
|
||||
"Only accept pokes with this buildkite token."
|
||||
), required=True,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--api-token", dest="api_token", help=(
|
||||
"API access token for buildkite. Require read_artifacts scope."
|
||||
"--webhook-token", dest="webhook_token", type=str, help=(
|
||||
"Only accept pokes signed with this github token."
|
||||
), required=True,
|
||||
)
|
||||
|
||||
# We require a matching webhook token, but because we take everything else
|
||||
# about what to deploy from the poke body, we can be a little more paranoid
|
||||
# and only accept builds / artifacts from a specific buildkite org
|
||||
# and only accept builds / artifacts from a specific github org
|
||||
parser.add_argument(
|
||||
"--org", dest="buildkite_org", help=(
|
||||
"Lock down to this buildkite org"
|
||||
"--org", dest="github_org", type=str, help=(
|
||||
"Lock down to this github org"
|
||||
)
|
||||
)
|
||||
# Optional matching workflow name
|
||||
parser.add_argument(
|
||||
"--workflow", dest="github_workflow", type=str, help=(
|
||||
"Lock down to this github workflow"
|
||||
)
|
||||
)
|
||||
|
||||
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
|
||||
arg_github_org = args.github_org
|
||||
arg_github_workflow = args.github_workflow
|
||||
|
||||
if not os.path.isdir(arg_extract_path):
|
||||
os.mkdir(arg_extract_path)
|
||||
|
||||
webhook.secret = args.webhook_token
|
||||
|
||||
deployer = Deployer()
|
||||
deployer.bundles_path = args.bundles_dir
|
||||
deployer.should_clean = args.clean
|
||||
|
||||
for include in args.include:
|
||||
for include in args.include.split(" "):
|
||||
deployer.symlink_paths.update({ os.path.basename(pth): pth for pth in glob.iglob(include) })
|
||||
|
||||
if args.tarball_uri is not None:
|
||||
build_dir = os.path.join(arg_extract_path, "test-%i" % (time.time()))
|
||||
deploy_tarball(args.tarball_uri, build_dir)
|
||||
else:
|
||||
print(
|
||||
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Include files: %s" %
|
||||
(args.port,
|
||||
arg_extract_path,
|
||||
" (clean after)" if deployer.should_clean else "",
|
||||
arg_symlink,
|
||||
deployer.symlink_paths,
|
||||
)
|
||||
print(
|
||||
"Listening on port %s. Extracting to %s%s. Symlinking to %s. Include files: %s" %
|
||||
(args.port,
|
||||
arg_extract_path,
|
||||
" (clean after)" if deployer.should_clean else "",
|
||||
arg_symlink,
|
||||
deployer.symlink_paths,
|
||||
)
|
||||
fred = threading.Thread(target=worker_thread)
|
||||
fred.daemon = True
|
||||
fred.start()
|
||||
app.run(port=args.port, debug=False)
|
||||
)
|
||||
|
||||
app.run(port=args.port, debug=False)
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
const path = require('path');
|
||||
const child_process = require('child_process');
|
||||
|
||||
const moduleName = process.argv[2];
|
||||
if (!moduleName) {
|
||||
console.error("Expected module name");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const argString = process.argv.length > 3 ? process.argv.slice(3).join(" ") : "";
|
||||
if (!argString) {
|
||||
console.error("Expected an yarn argument string to use");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const modulePath = path.dirname(require.resolve(`${moduleName}/package.json`));
|
||||
|
||||
child_process.execSync("yarn " + argString, {
|
||||
env: process.env,
|
||||
cwd: modulePath,
|
||||
stdio: ['inherit', 'inherit', 'inherit'],
|
||||
});
|
||||
14
sonar-project.properties
Normal file
14
sonar-project.properties
Normal file
@@ -0,0 +1,14 @@
|
||||
sonar.projectKey=element-web
|
||||
sonar.organization=new_vector_ltd_organization
|
||||
|
||||
# Encoding of the source code. Default is default system encoding
|
||||
#sonar.sourceEncoding=UTF-8
|
||||
|
||||
sonar.sources=src,res
|
||||
sonar.tests=test
|
||||
sonar.exclusions=__mocks__,docs,element.io,nginx
|
||||
|
||||
sonar.typescript.tsconfigPath=./tsconfig.json
|
||||
sonar.javascript.lcov.reportPaths=coverage/lcov.info
|
||||
sonar.coverage.exclusions=test/**/*,res/**/*
|
||||
sonar.testExecutionReportPaths=coverage/test-report.xml
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"Dismiss": "Rezigni",
|
||||
"powered by Matrix": "povigita per Matrix",
|
||||
"Unknown device": "Nekonata aparato",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Vi devas uzi HTTPS por ekran-kundivide alvoki.",
|
||||
"Welcome to Element": "Bonvenon al Element",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Malcentra, ĉifrita babilado & kunlaboro povigita per [matrix]",
|
||||
"Sign In": "Ensaluti",
|
||||
"Create Account": "Krei konton",
|
||||
"Explore rooms": "Esplori ĉambrojn",
|
||||
@@ -14,10 +12,7 @@
|
||||
"The message from the parser is: %(message)s": "La mesaĝo el la analizilo estas: %(message)s",
|
||||
"Invalid JSON": "Nevalida JSON",
|
||||
"Go to your browser to complete Sign In": "Iru al via retumilo por finpretigi la ensaluton",
|
||||
"Open user settings": "Malfermi agordojn de uzanto",
|
||||
"Unable to load config file: please refresh the page to try again.": "Ne povas enlegi agordan dosieron: bonvolu reprovi per aktualigo de la paĝo.",
|
||||
"Previous/next recently visited room or community": "Antaŭa/sekva freŝe vizitita ĉambro aŭ komunumo",
|
||||
"Missing indexeddb worker script!": "Mankas fonskripto «indexeddb»!",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s labortabla (%(platformName)s)",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"Unsupported browser": "Nesubtenata retumilo",
|
||||
@@ -34,6 +29,5 @@
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s uzas specialajn funkciojn de retumilo, kiujn via nuna retumilo ne subtenas.",
|
||||
"Powered by Matrix": "Povigata de Matrix",
|
||||
"Use %(brand)s on mobile": "Uzi %(brand)s poŝtelefone",
|
||||
"Switch to space by number": "Baskuli al aro laŭ numero",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Malcentralizita kaj ĉifrita babilejo; kunlaboro danke al $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
{
|
||||
"powered by Matrix": "keyrt með Matrix",
|
||||
"Welcome to Element": "Velkomin í Element",
|
||||
"Unknown device": "Óþekkt tæki",
|
||||
"Dismiss": "Hunsa",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Þú verður að nota HTTPS til að hringja símtal með skjádeilingu.",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Dulritað dreifvinnsluspjall og samvinnutól keyrt með [matrix]",
|
||||
"Open": "Opna",
|
||||
"Unsupported browser": "Óstuddur vafri",
|
||||
"Your browser can't run %(brand)s": "Vafrinn þinn getur ekki keyrt %(brand)s",
|
||||
"Sign In": "Skrá inn",
|
||||
"Create Account": "Búa til notandaaðgang",
|
||||
"Explore rooms": "Kanna spjallrásir",
|
||||
"Missing indexeddb worker script!": "Að vanta indexeddb vinnumaður tölvuhandrit!",
|
||||
"The message from the parser is: %(message)s": "Skilaboðið frá þáttaranum er %(message)s",
|
||||
"Invalid JSON": "Ógilt JSON",
|
||||
"Download Completed": "Niðurhali lokið",
|
||||
@@ -26,16 +23,11 @@
|
||||
"Powered by Matrix": "Keyrt með Matrix",
|
||||
"Go to your browser to complete Sign In": "Farðu í vafrann þinn til að ljúka innskráningu",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Desktop fyrir vinnutölvur (%(platformName)s)",
|
||||
"Previous/next recently visited room or community": "Fyrra/næsta nýlega heimsótt herbergi eða samfélag",
|
||||
"Open user settings": "Opna notandastillingar",
|
||||
"Unable to load config file: please refresh the page to try again.": "Ekki er hægt að hlaða stillingaskrána: endurnýjaðu síðuna til að reyna aftur.",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Element-stillingar þínar innihalda ógilt JSON. Leiðréttu vandamálið og endurlestu síðuna.",
|
||||
"Your Element is misconfigured": "Element-tilvikið þitt er rangt stillt",
|
||||
"Invalid configuration: no default server specified.": "Ógild uppsetning: enginn sjálfgefinn vefþjónn tilgreindur.",
|
||||
"Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Ógild uppsetning: getur aðeins tilgreint eitt af default_server_config, default_server_name eða default_hs_url.",
|
||||
"Use %(brand)s on mobile": "Nota %(brand)s í síma",
|
||||
"Next recently visited room or community": "Næsta nýlega heimsótt rými eða samfélag",
|
||||
"Previous recently visited room or community": "Fyrra nýlega heimsótt rými eða samfélag",
|
||||
"Switch to space by number": "Skipta yfir í rými með númeri",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Dreifstýrt, dulritað spjall og samskipti keyrt með $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"Dismiss": "Dispensar",
|
||||
"powered by Matrix": "oferecido por Matrix",
|
||||
"Unknown device": "Dispositivo desconhecido",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Você precisa estar usando HTTPS para começar uma chamada de compartilhamento de tela.",
|
||||
"Welcome to Element": "Boas-vindas a Element",
|
||||
"Decentralised, encrypted chat & collaboration powered by [matrix]": "Chat & colaboração descentralizados e encriptados powered by [matrix]",
|
||||
"Sign In": "Fazer signin",
|
||||
"Create Account": "Criar Conta",
|
||||
"Explore rooms": "Explorar salas",
|
||||
@@ -15,7 +13,6 @@
|
||||
"Invalid configuration: no default server specified.": "Configuração inválida: nenhum servidor default especificado.",
|
||||
"Unable to load config file: please refresh the page to try again.": "Incapaz de carregar arquivo de config: por favor atualize a página para tentar de novo.",
|
||||
"Download Completed": "Download Completado",
|
||||
"Open user settings": "Abrir configurações de usuária(o)",
|
||||
"%(appName)s (%(browserName)s, %(osName)s)": "%(appName)s (%(browserName)s, %(osName)s)",
|
||||
"Unsupported browser": "Browser insuportado",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Por favor instale <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ou <safariLink>Safari</safariLink> para a melhor experiência.",
|
||||
@@ -23,9 +20,7 @@
|
||||
"I understand the risks and wish to continue": "Eu entendo os riscos e desejo continuar",
|
||||
"Go to element.io": "Ir para element.io",
|
||||
"Failed to start": "Falha para iniciar",
|
||||
"Missing indexeddb worker script!": "Worker script indexeddb faltando!",
|
||||
"Open": "Abrir",
|
||||
"Previous/next recently visited room or community": "Anterior/próxima sala ou comunidade visitada recentemente",
|
||||
"%(brand)s Desktop (%(platformName)s)": "%(brand)s Desktop (%(platformName)s)",
|
||||
"Go to your browser to complete Sign In": "Vá para seu browser para completar Sign In",
|
||||
"Your Element is misconfigured": "Seu Element está malconfigurado",
|
||||
@@ -34,8 +29,5 @@
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s usa funcionalidade de browser avançada que não é suportada por seu browser atual.",
|
||||
"Powered by Matrix": "Powered by Matrix",
|
||||
"Use %(brand)s on mobile": "Usar %(brand)s em celular",
|
||||
"Switch to space by number": "Trocar para espaço por número",
|
||||
"Next recently visited room or community": "Próxima sala ou comunidade recentemente visitada",
|
||||
"Previous recently visited room or community": "Anterior sala ou comunidade recentemente visitada",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Chat descentralizado e encriptado & colaboração, powered by $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"Unknown device": "Thiết bị không xác định",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Bạn phải sử dụng mã hóa HTTPS để thực hiện chia sẻ màn hình khi gọi.",
|
||||
"You need to be using HTTPS to place a screen-sharing call.": "Bạn cần sử dụng giao thức HTTPS để thực hiện một cuộc gọi có chia sẻ màn hình.",
|
||||
"Dismiss": "Bỏ qua",
|
||||
"powered by Matrix": "tài trợ bởi Matrix",
|
||||
"Welcome to Element": "Chào mừng tới Element",
|
||||
@@ -14,9 +14,9 @@
|
||||
"Create Account": "Tạo tài khoản",
|
||||
"Explore rooms": "Khám phá phòng chat",
|
||||
"Download Completed": "Tải xuống hoàn tất",
|
||||
"Go to element.io": "Mở element.io",
|
||||
"Go to element.io": "Đi đến element.io",
|
||||
"I understand the risks and wish to continue": "Tôi hiểu các rủi ro và muốn tiếp tục",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Bạn có thể tiếp tục sử dụng trình duyệt hiện tại của bạn, nhưng một số tính năng có thể không hoạt động và trải nghiệm ứng dụng có thể sẽ không được tốt.",
|
||||
"You can continue using your current browser, but some or all features may not work and the look and feel of the application may be incorrect.": "Bạn có thể tiếp tục sử dụng trình duyệt hiện tại, tuy nhiên một số hoặc tất cả tính năng có thể sẽ không hoạt động và trải nghiệm ứng dụng có thể sẽ không được tốt.",
|
||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Hãy cài đặt <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, hoặc <safariLink>Safari</safariLink> để có trải nghiệm tốt nhất.",
|
||||
"%(brand)s uses advanced browser features which aren't supported by your current browser.": "%(brand)s sử dụng một số tính năng nâng cao mà trình duyệt của bạn không thể đáp ứng.",
|
||||
"Your browser can't run %(brand)s": "Trình duyệt của bạn không thể chạy %(brand)s",
|
||||
@@ -34,5 +34,6 @@
|
||||
"Missing indexeddb worker script!": "Thiếu tệp lệnh làm việc của indexeddb!",
|
||||
"Your Element configuration contains invalid JSON. Please correct the problem and reload the page.": "Thiết lập Element của bạn chứa JSON không hợp lệ. Vui lòng sửa vấn đề và tải lại trang.",
|
||||
"Your Element is misconfigured": "Element của bạn bị thiết lập sai",
|
||||
"Switch to space by number": "Chuyển sang Space bằng số"
|
||||
"Switch to space by number": "Chuyển sang Space bằng số",
|
||||
"Decentralised, encrypted chat & collaboration powered by $matrixLogo": "Dịch vụ chat & liên lạc đã được mã hóa, phi tập trung. Được cung cấp bởi $matrixLogo"
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
if (!optional && vals.length !== 1) {
|
||||
throw new Error(`Expected singular ${name} in query string`);
|
||||
}
|
||||
return <string>vals[0];
|
||||
return vals[0];
|
||||
};
|
||||
|
||||
// If we have these params, expect a widget API to be available (ie. to be in an iframe
|
||||
@@ -156,6 +156,15 @@ const ack = (ev: CustomEvent<IWidgetApiRequest>) => widgetApi.transport.reply(ev
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.ForceHangupCall}`,
|
||||
(ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
meetApi?.dispose();
|
||||
notifyHangup();
|
||||
meetApi = null;
|
||||
closeConference();
|
||||
ack(ev);
|
||||
},
|
||||
);
|
||||
widgetApi.on(`action:${ElementWidgetActions.MuteAudio}`,
|
||||
async (ev: CustomEvent<IWidgetApiRequest>) => {
|
||||
ack(ev);
|
||||
@@ -241,7 +250,9 @@ function switchVisibleContainers() {
|
||||
|
||||
function toggleConferenceVisibility(inConference: boolean) {
|
||||
document.getElementById("jitsiContainer").style.visibility = inConference ? 'unset' : 'hidden';
|
||||
document.getElementById("joinButtonContainer").style.visibility = inConference ? 'hidden' : 'unset';
|
||||
// Video rooms have a separate UI for joining, so they should never show our join button
|
||||
document.getElementById("joinButtonContainer").style.visibility =
|
||||
(inConference || isVideoChannel) ? 'hidden' : 'unset';
|
||||
}
|
||||
|
||||
function skipToJitsiSplashScreen() {
|
||||
@@ -289,18 +300,27 @@ function createJWTToken() {
|
||||
);
|
||||
}
|
||||
|
||||
async function notifyHangup() {
|
||||
async function notifyHangup(errorMessage?: string) {
|
||||
if (widgetApi) {
|
||||
// We send the hangup event before setAlwaysOnScreen, because the latter
|
||||
// can cause the receiving side to instantly stop listening.
|
||||
try {
|
||||
await widgetApi.transport.send(ElementWidgetActions.HangupCall, {});
|
||||
await widgetApi.transport.send(ElementWidgetActions.HangupCall, { errorMessage });
|
||||
} finally {
|
||||
await widgetApi.setAlwaysOnScreen(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeConference() {
|
||||
switchVisibleContainers();
|
||||
document.getElementById("jitsiContainer").innerHTML = "";
|
||||
|
||||
if (skipOurWelcomeScreen) {
|
||||
skipToJitsiSplashScreen();
|
||||
}
|
||||
}
|
||||
|
||||
// event handler bound in HTML
|
||||
function joinConference(audioDevice?: string, videoDevice?: string) {
|
||||
let jwt;
|
||||
@@ -344,8 +364,14 @@ function joinConference(audioDevice?: string, videoDevice?: string) {
|
||||
configOverwrite: {
|
||||
subject: roomName,
|
||||
startAudioOnly,
|
||||
startWithAudioMuted: !audioDevice,
|
||||
startWithVideoMuted: !videoDevice,
|
||||
startWithAudioMuted: audioDevice == null,
|
||||
startWithVideoMuted: videoDevice == 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
|
||||
// back over the iframe API, and therefore end up crashing
|
||||
// https://github.com/jitsi/jitsi-meet/issues/11585
|
||||
apiLogLevels: ["warn", "error"],
|
||||
} as any,
|
||||
jwt: jwt,
|
||||
};
|
||||
@@ -386,26 +412,21 @@ function joinConference(audioDevice?: string, videoDevice?: string) {
|
||||
meetApi = null;
|
||||
});
|
||||
|
||||
meetApi.on("readyToClose", () => {
|
||||
switchVisibleContainers();
|
||||
document.getElementById("jitsiContainer").innerHTML = "";
|
||||
|
||||
if (skipOurWelcomeScreen) {
|
||||
skipToJitsiSplashScreen();
|
||||
}
|
||||
});
|
||||
meetApi.on("readyToClose", closeConference);
|
||||
|
||||
meetApi.on("errorOccurred", ({ error }) => {
|
||||
if (error.isFatal) {
|
||||
// We got disconnected. Since Jitsi Meet might send us back to the
|
||||
// prejoin screen, we're forced to act as if we hung up entirely.
|
||||
notifyHangup();
|
||||
notifyHangup(error.message);
|
||||
meetApi = null;
|
||||
closeConference();
|
||||
}
|
||||
});
|
||||
|
||||
meetApi.on("audioMuteStatusChanged", ({ muted }) => {
|
||||
const action = muted ? ElementWidgetActions.MuteAudio : ElementWidgetActions.UnmuteAudio;
|
||||
widgetApi.transport.send(action, {});
|
||||
widgetApi?.transport.send(action, {});
|
||||
});
|
||||
|
||||
meetApi.on("videoMuteStatusChanged", ({ muted }) => {
|
||||
@@ -415,10 +436,10 @@ function joinConference(audioDevice?: string, videoDevice?: string) {
|
||||
// otherwise the React SDK will mistakenly think the user turned off
|
||||
// their video by hand
|
||||
setTimeout(() => {
|
||||
if (meetApi) widgetApi.transport.send(ElementWidgetActions.MuteVideo, {});
|
||||
if (meetApi) widgetApi?.transport.send(ElementWidgetActions.MuteVideo, {});
|
||||
}, 200);
|
||||
} else {
|
||||
widgetApi.transport.send(ElementWidgetActions.UnmuteVideo, {});
|
||||
widgetApi?.transport.send(ElementWidgetActions.UnmuteVideo, {});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -429,4 +450,9 @@ function joinConference(audioDevice?: string, videoDevice?: string) {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Patch logs into rageshakes
|
||||
meetApi.on("log", ({ logLevel, args }) =>
|
||||
(parent as unknown as typeof global).mx_rage_logger?.log(logLevel, ...args),
|
||||
);
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -17,7 +17,7 @@ function renderConfigError(message: string): void {
|
||||
const toHide = document.getElementsByClassName("mx_HomePage_container");
|
||||
const errorContainers = document.getElementsByClassName(
|
||||
"mx_HomePage_errorContainer",
|
||||
) as HTMLCollectionOf<HTMLDialogElement>;
|
||||
) as HTMLCollectionOf<HTMLDivElement>;
|
||||
|
||||
for (const e of toHide) {
|
||||
// We have to clear the content because .style.display='none'; doesn't work
|
||||
|
||||
@@ -414,6 +414,18 @@ export default class ElectronPlatform extends VectorBasePlatform {
|
||||
return this.ipcCall('setMinimizeToTrayEnabled', enabled);
|
||||
}
|
||||
|
||||
public supportsTogglingHardwareAcceleration(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public async getHardwareAccelerationEnabled(): Promise<boolean> {
|
||||
return this.ipcCall('getHardwareAccelerationEnabled');
|
||||
}
|
||||
|
||||
public async setHardwareAccelerationEnabled(enabled: boolean): Promise<void> {
|
||||
return this.ipcCall('setHardwareAccelerationEnabled', enabled);
|
||||
}
|
||||
|
||||
async canSelfUpdate(): Promise<boolean> {
|
||||
const feedUrl = await this.ipcCall('getUpdateFeedUrl');
|
||||
return Boolean(feedUrl);
|
||||
|
||||
@@ -106,11 +106,10 @@ export default class WebPlatform extends VectorBasePlatform {
|
||||
}
|
||||
|
||||
getNormalizedAppVersion(version: string): string {
|
||||
// if version looks like semver with leading v, strip it
|
||||
// (matches scripts/normalize-version.sh)
|
||||
const semVerRegex = new RegExp("^v[0-9]+.[0-9]+.[0-9]+(-.+)?$");
|
||||
// if version looks like semver with leading v, strip it (matches scripts/normalize-version.sh)
|
||||
const semVerRegex = /^v\d+.\d+.\d+(-.+)?$/;
|
||||
if (semVerRegex.test(version)) {
|
||||
return version.substr(1);
|
||||
return version.substring(1);
|
||||
}
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -53,10 +53,6 @@
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.mx_HomePage_header {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_HomePage_col {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -144,7 +140,7 @@
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.92 33.32C20.92 34.2478 20.1679 35 19.24 35C13.0544 35 8.04001 29.9856 8.04001 23.8C8.04001 22.8722 8.79217 22.12 9.72001 22.12C10.6478 22.12 11.4 22.8722 11.4 23.8C11.4 28.1299 14.9101 31.64 19.24 31.64C20.1679 31.64 20.92 32.3922 20.92 33.32Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.68 24.9199C3.75216 24.9199 3 24.1678 3 23.2399C3 17.0543 8.01441 12.0399 14.2 12.0399C15.1278 12.0399 15.88 12.7921 15.88 13.7199C15.88 14.6478 15.1278 15.3999 14.2 15.3999C9.87009 15.3999 6.36 18.91 6.36 23.2399C6.36 24.1678 5.60784 24.9199 4.68 24.9199Z" fill="#0DBD8B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.32 17.0801C30.2478 17.0801 31 17.8322 31 18.7601C31 24.9457 25.9856 29.9601 19.8 29.9601C18.8722 29.9601 18.12 29.2079 18.12 28.2801C18.12 27.3522 18.8722 26.6001 19.8 26.6001C24.1299 26.6001 27.64 23.09 27.64 18.7601C27.64 17.8322 28.3922 17.0801 29.32 17.0801Z" fill="#0DBD8B"/>
|
||||
</svg>
|
||||
</svg>
|
||||
</span>
|
||||
<h1>Unsupported browser</h1>
|
||||
</div>
|
||||
|
||||
@@ -54,10 +54,6 @@
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.mx_HomePage_header {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mx_HomePage_col {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
122
test/unit-tests/vector/getconfig-test.ts
Normal file
122
test/unit-tests/vector/getconfig-test.ts
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
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 request from 'browser-request';
|
||||
|
||||
import { getVectorConfig } from "../../../src/vector/getconfig";
|
||||
|
||||
describe('getVectorConfig()', () => {
|
||||
const setRequestMockImplementationOnce = (err?: unknown, response?: { status: number }, body?: string) =>
|
||||
request.mockImplementationOnce((_opts, callback) => callback(err, response, body));
|
||||
|
||||
const prevDocumentDomain = document.domain;
|
||||
const elementDomain = 'app.element.io';
|
||||
const now = 1234567890;
|
||||
const specificConfig = {
|
||||
brand: 'specific',
|
||||
}
|
||||
const generalConfig = {
|
||||
brand: 'general',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
document.domain = elementDomain;
|
||||
|
||||
// stable value for cachebuster
|
||||
jest.spyOn(Date, 'now').mockReturnValue(now);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
document.domain = prevDocumentDomain;
|
||||
jest.spyOn(Date, 'now').mockRestore();
|
||||
});
|
||||
|
||||
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' }))
|
||||
expect(request.mock.calls[1][0]).toEqual(expect.objectContaining({ url: '../config.json' }))
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('rejects with an error when general config rejects', async () => {
|
||||
setRequestMockImplementationOnce('err-specific');
|
||||
setRequestMockImplementationOnce('err-general');
|
||||
|
||||
await expect(() => getVectorConfig()).rejects.toEqual({"err": "err-general", "response": undefined});
|
||||
});
|
||||
|
||||
});
|
||||
186
test/unit-tests/vector/platform/WebPlatform-test.ts
Normal file
186
test/unit-tests/vector/platform/WebPlatform-test.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
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 request from 'browser-request';
|
||||
import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform';
|
||||
import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg';
|
||||
|
||||
import WebPlatform from '../../../../src/vector/platform/WebPlatform';
|
||||
|
||||
describe('WebPlatform', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('returns human readable name', () => {
|
||||
const platform = new WebPlatform();
|
||||
expect(platform.getHumanReadableName()).toEqual('Web Platform');
|
||||
});
|
||||
|
||||
describe('notification support', () => {
|
||||
const mockNotification = {
|
||||
requestPermission: jest.fn(),
|
||||
permission: 'notGranted',
|
||||
}
|
||||
beforeEach(() => {
|
||||
// @ts-ignore
|
||||
window.Notification = mockNotification;
|
||||
mockNotification.permission = 'notGranted';
|
||||
});
|
||||
|
||||
it('supportsNotifications returns false when platform does not support notifications', () => {
|
||||
// @ts-ignore
|
||||
window.Notification = undefined;
|
||||
expect(new WebPlatform().supportsNotifications()).toBe(false);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
it('maySendNotifications returns true when notification permissions are granted', () => {
|
||||
mockNotification.permission = 'granted'
|
||||
expect(new WebPlatform().maySendNotifications()).toBe(true);
|
||||
});
|
||||
|
||||
it('requests notification permissions and returns result ', async () => {
|
||||
mockNotification.requestPermission.mockImplementation(callback => callback('test'));
|
||||
|
||||
const platform = new WebPlatform();
|
||||
const result = await platform.requestNotificationPermission();
|
||||
expect(result).toEqual('test');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('app version', () => {
|
||||
const envVersion = process.env.VERSION;
|
||||
const prodVersion = '1.10.13';
|
||||
|
||||
const setRequestMockImplementation = (err?: unknown, response?: { status: number }, body?: string) =>
|
||||
request.mockImplementation((_opts, callback) => callback(err, response, body));
|
||||
|
||||
beforeEach(() => {
|
||||
jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(false);
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
process.env.VERSION = envVersion;
|
||||
});
|
||||
|
||||
it('should return true from canSelfUpdate()', async () => {
|
||||
const platform = new WebPlatform();
|
||||
const result = await platform.canSelfUpdate();
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('getAppVersion returns normalized app version', async () => {
|
||||
process.env.VERSION = prodVersion;
|
||||
const platform = new WebPlatform();
|
||||
|
||||
const version = await platform.getAppVersion();
|
||||
expect(version).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `v${prodVersion}`;
|
||||
const version2 = await platform.getAppVersion();
|
||||
// v prefix removed
|
||||
expect(version2).toEqual(prodVersion);
|
||||
|
||||
process.env.VERSION = `version not like semver`;
|
||||
const notSemverVersion = await platform.getAppVersion();
|
||||
expect(notSemverVersion).toEqual(`version not like semver`);
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
@@ -34,8 +34,7 @@ const cssThemes = {
|
||||
function getActiveThemes() {
|
||||
// Default to `light` theme when the MATRIX_THEMES environment variable is not defined.
|
||||
const theme = process.env.MATRIX_THEMES ?? 'light';
|
||||
const themes = theme.split(',').filter(x => x).map(x => x.trim()).filter(x => x);
|
||||
return themes;
|
||||
return theme.split(',').map(x => x.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
// See docs/customisations.md
|
||||
@@ -80,7 +79,6 @@ module.exports = (env, argv) => {
|
||||
const nodeEnv = argv.mode;
|
||||
const devMode = nodeEnv !== 'production';
|
||||
const useHMR = process.env.CSS_HOT_RELOAD === '1' && devMode;
|
||||
const fullPageErrors = process.env.FULL_PAGE_ERRORS === '1' && devMode;
|
||||
const enableMinification = !devMode && !process.env.CI_PACKAGE;
|
||||
|
||||
const development = {};
|
||||
@@ -99,17 +97,16 @@ module.exports = (env, argv) => {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve the directories for the react-sdk and js-sdk for later use. We resolve these early so we
|
||||
// Resolve the directories for the react-sdk and js-sdk for later use. We resolve these early, so we
|
||||
// don't have to call them over and over. We also resolve to the package.json instead of the src
|
||||
// directory so we don't have to rely on a index.js or similar file existing.
|
||||
// directory, so we don't have to rely on an index.js or similar file existing.
|
||||
const reactSdkSrcDir = path.resolve(require.resolve("matrix-react-sdk/package.json"), '..', 'src');
|
||||
const jsSdkSrcDir = path.resolve(require.resolve("matrix-js-sdk/package.json"), '..', 'src');
|
||||
|
||||
const ACTIVE_THEMES = getActiveThemes();
|
||||
function getThemesImports() {
|
||||
const imports = ACTIVE_THEMES.map((t, index) => {
|
||||
const themeImportPath = cssThemes[`theme-${ t }`].replace('./node_modules/', '');
|
||||
return themeImportPath;
|
||||
const imports = ACTIVE_THEMES.map((t) => {
|
||||
return cssThemes[`theme-${ t }`].replace('./node_modules/', ''); // theme import path
|
||||
});
|
||||
const s = JSON.stringify(ACTIVE_THEMES);
|
||||
return `
|
||||
@@ -483,7 +480,7 @@ module.exports = (env, argv) => {
|
||||
esModule: false,
|
||||
name: '[name].[hash:7].[ext]',
|
||||
outputPath: getAssetOutputPath,
|
||||
publicPath: function (url, resourcePath) {
|
||||
publicPath: function(url, resourcePath) {
|
||||
const outputPath = getAssetOutputPath(url, resourcePath);
|
||||
return toPublicPath(outputPath);
|
||||
},
|
||||
@@ -495,13 +492,13 @@ module.exports = (env, argv) => {
|
||||
esModule: false,
|
||||
name: '[name].[hash:7].[ext]',
|
||||
outputPath: getAssetOutputPath,
|
||||
publicPath: function (url, resourcePath) {
|
||||
publicPath: function(url, resourcePath) {
|
||||
const outputPath = getAssetOutputPath(url, resourcePath);
|
||||
return toPublicPath(outputPath);
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
@@ -513,7 +510,7 @@ module.exports = (env, argv) => {
|
||||
esModule: false,
|
||||
name: '[name].[hash:7].[ext]',
|
||||
outputPath: getAssetOutputPath,
|
||||
publicPath: function (url, resourcePath) {
|
||||
publicPath: function(url, resourcePath) {
|
||||
// CSS image usages end up in the `bundles/[hash]` output
|
||||
// directory, so we adjust the final path to navigate up
|
||||
// twice.
|
||||
@@ -522,7 +519,7 @@ module.exports = (env, argv) => {
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(gif|png|ttf|woff|woff2|xml|ico)$/,
|
||||
|
||||
Reference in New Issue
Block a user