Merge branch 'develop' of https://github.com/vector-im/element-web into t3chguy/dedup-icons-17oct
# Conflicts: # res/css/structures/_GenericDropdownMenu.pcss # res/css/views/elements/_ImageView.pcss # res/css/views/rooms/_MessageComposer.pcss # src/components/structures/RoomStatusBar.tsx # src/components/views/elements/Pill.tsx # src/components/views/messages/MessageActionBar.tsx # src/components/views/rooms/RoomKnocksBar.tsx # src/components/views/rooms/RoomTileSubtitle.tsx # src/components/views/settings/tabs/room/PeopleRoomSettingsTab.tsx
@@ -7,3 +7,6 @@ karma-reports/
|
|||||||
.idea/
|
.idea/
|
||||||
.tmp/
|
.tmp/
|
||||||
config.json*
|
config.json*
|
||||||
|
# Exclude the playwright directory as much as we can as the snapshots are huge and we bind mount it in
|
||||||
|
playwright/
|
||||||
|
!playwright/docker-entrypoint.sh
|
||||||
|
|||||||
@@ -198,6 +198,8 @@ module.exports = {
|
|||||||
"@typescript-eslint/ban-ts-comment": "off",
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
// We're okay with assertion errors when we ask for them
|
// We're okay with assertion errors when we ask for them
|
||||||
"@typescript-eslint/no-non-null-assertion": "off",
|
"@typescript-eslint/no-non-null-assertion": "off",
|
||||||
|
// We do this sometimes to brand interfaces
|
||||||
|
"@typescript-eslint/no-empty-object-type": "off",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// temporary override for offending icon require files
|
// temporary override for offending icon require files
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
backport:
|
backport:
|
||||||
name: Backport
|
name: Backport
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
# Only react to merged PRs for security reasons.
|
# Only react to merged PRs for security reasons.
|
||||||
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
|
||||||
if: >
|
if: >
|
||||||
|
|||||||
@@ -18,15 +18,15 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image:
|
image:
|
||||||
- ubuntu-latest
|
- ubuntu-24.04
|
||||||
- windows-latest
|
- windows-2022
|
||||||
- macos-latest
|
- macos-14
|
||||||
isDevelop:
|
isDevelop:
|
||||||
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
|
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
|
||||||
# Skip the ubuntu-latest build for the develop branch as the dedicated CD build_develop workflow handles that
|
# Skip the ubuntu-24.04 build for the develop branch as the dedicated CD build_develop workflow handles that
|
||||||
exclude:
|
exclude:
|
||||||
- isDevelop: true
|
- isDevelop: true
|
||||||
image: ubuntu-latest
|
image: ubuntu-24.04
|
||||||
runs-on: ${{ matrix.image }}
|
runs-on: ${{ matrix.image }}
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: Build package
|
name: Build package
|
||||||
environment: packages.element.io
|
environment: packages.element.io
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
env:
|
env:
|
||||||
R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }}
|
R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }}
|
||||||
R2_URL: ${{ vars.CF_R2_S3_API }}
|
R2_URL: ${{ vars.CF_R2_S3_API }}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
name: "Build & Deploy develop.element.io"
|
name: "Build & Deploy develop.element.io"
|
||||||
# Only respect triggers from our develop branch, ignore that of forks
|
# Only respect triggers from our develop branch, ignore that of forks
|
||||||
if: github.repository == 'element-hq/element-web'
|
if: github.repository == 'element-hq/element-web'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
environment: develop
|
environment: develop
|
||||||
env:
|
env:
|
||||||
R2_BUCKET: "element-web-develop"
|
R2_BUCKET: "element-web-develop"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ permissions:
|
|||||||
jobs:
|
jobs:
|
||||||
buildx:
|
buildx:
|
||||||
name: Docker Buildx
|
name: Docker Buildx
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
environment: dockerhub
|
environment: dockerhub
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: GitHub Pages
|
name: GitHub Pages
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Fetch element-desktop
|
- name: Fetch element-desktop
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
environment:
|
environment:
|
||||||
name: github-pages
|
name: github-pages
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs: build
|
needs: build
|
||||||
steps:
|
steps:
|
||||||
- name: Deploy to GitHub Pages
|
- name: Deploy to GitHub Pages
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
tidy:
|
tidy:
|
||||||
name: Tidy closed issues
|
name: Tidy closed issues
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v7
|
||||||
id: main
|
id: main
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ concurrency: ${{ github.workflow }}
|
|||||||
jobs:
|
jobs:
|
||||||
bot:
|
bot:
|
||||||
name: Pending reviews bot
|
name: Pending reviews bot
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
environment: Matrix
|
environment: Matrix
|
||||||
env:
|
env:
|
||||||
URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+"
|
URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+"
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ jobs:
|
|||||||
notify-downstream:
|
notify-downstream:
|
||||||
name: Trigger release drafter downstream
|
name: Trigger release drafter downstream
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Notify element-desktop repo that element-web release has completed to re-trigger release-drafter
|
- name: Notify element-desktop repo that element-web release has completed to re-trigger release-drafter
|
||||||
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1
|
uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1
|
||||||
@@ -41,7 +41,7 @@ jobs:
|
|||||||
check:
|
check:
|
||||||
name: Post release checks
|
name: Post release checks
|
||||||
needs: release
|
needs: release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Wait for dockerhub
|
- name: Wait for dockerhub
|
||||||
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ on:
|
|||||||
default: true
|
default: true
|
||||||
jobs:
|
jobs:
|
||||||
prepare:
|
prepare:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Element Desktop
|
- name: Checkout Element Desktop
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
ts_lint:
|
ts_lint:
|
||||||
name: "Typescript Syntax Check"
|
name: "Typescript Syntax Check"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ jobs:
|
|||||||
|
|
||||||
js_lint:
|
js_lint:
|
||||||
name: "ESLint"
|
name: "ESLint"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ jobs:
|
|||||||
|
|
||||||
style_lint:
|
style_lint:
|
||||||
name: "Style Lint"
|
name: "Style Lint"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ jobs:
|
|||||||
|
|
||||||
workflow_lint:
|
workflow_lint:
|
||||||
name: "Workflow Lint"
|
name: "Workflow Lint"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
@@ -135,7 +135,7 @@ jobs:
|
|||||||
|
|
||||||
analyse_dead_code:
|
analyse_dead_code:
|
||||||
name: "Analyse Dead Code"
|
name: "Analyse Dead Code"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
web-app-team:
|
web-app-team:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: |
|
if: |
|
||||||
contains(github.event.issue.assignees.*.login, 't3chguy') ||
|
contains(github.event.issue.assignees.*.login, 't3chguy') ||
|
||||||
contains(github.event.issue.assignees.*.login, 'andybalaam') ||
|
contains(github.event.issue.assignees.*.login, 'andybalaam') ||
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
automate-project-columns:
|
automate-project-columns:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/add-to-project@main
|
- uses: actions/add-to-project@main
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
apply_Z-Labs_label:
|
apply_Z-Labs_label:
|
||||||
name: Add Z-Labs label for features behind labs flags
|
name: Add Z-Labs label for features behind labs flags
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
apply_Help-Wanted_label:
|
apply_Help-Wanted_label:
|
||||||
name: Add "Help Wanted" label to all "good first issue" and Hacktoberfest
|
name: Add "Help Wanted" label to all "good first issue" and Hacktoberfest
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'good first issue') ||
|
contains(github.event.issue.labels.*.name, 'good first issue') ||
|
||||||
contains(github.event.issue.labels.*.name, 'Hacktoberfest')
|
contains(github.event.issue.labels.*.name, 'Hacktoberfest')
|
||||||
@@ -54,7 +54,7 @@ jobs:
|
|||||||
|
|
||||||
move_needs_info_issues:
|
move_needs_info_issues:
|
||||||
name: X-Needs-Info issues to Need info column on triage board
|
name: X-Needs-Info issues to Need info column on triage board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
|
||||||
steps:
|
steps:
|
||||||
@@ -77,7 +77,7 @@ jobs:
|
|||||||
|
|
||||||
move_flakey_test_issues:
|
move_flakey_test_issues:
|
||||||
name: Z-Flaky-Test issues to Sized for maintainer column on triage board
|
name: Z-Flaky-Test issues to Sized for maintainer column on triage board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
|
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
|
||||||
steps:
|
steps:
|
||||||
@@ -100,7 +100,7 @@ jobs:
|
|||||||
|
|
||||||
add_priority_design_issues_to_project:
|
add_priority_design_issues_to_project:
|
||||||
name: P1 X-Needs-Design to Design project board
|
name: P1 X-Needs-Design to Design project board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
|
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
|
||||||
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
|
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
|
||||||
@@ -117,7 +117,7 @@ jobs:
|
|||||||
|
|
||||||
add_product_issues:
|
add_product_issues:
|
||||||
name: X-Needs-Product to product project board
|
name: X-Needs-Product to product project board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
|
||||||
steps:
|
steps:
|
||||||
@@ -128,7 +128,7 @@ jobs:
|
|||||||
|
|
||||||
Search_issues_to_board:
|
Search_issues_to_board:
|
||||||
name: Search issues to project board
|
name: Search issues to project board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
|
||||||
steps:
|
steps:
|
||||||
@@ -139,7 +139,7 @@ jobs:
|
|||||||
|
|
||||||
voip:
|
voip:
|
||||||
name: Add labelled issues to VoIP project board
|
name: Add labelled issues to VoIP project board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'Team: VoIP')
|
contains(github.event.issue.labels.*.name, 'Team: VoIP')
|
||||||
steps:
|
steps:
|
||||||
@@ -150,7 +150,7 @@ jobs:
|
|||||||
|
|
||||||
verticals_feature:
|
verticals_feature:
|
||||||
name: Add labelled issues to Verticals Feature project
|
name: Add labelled issues to Verticals Feature project
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
|
contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
|
||||||
steps:
|
steps:
|
||||||
@@ -161,7 +161,7 @@ jobs:
|
|||||||
|
|
||||||
tech_debt:
|
tech_debt:
|
||||||
name: Add labelled issues to tech debt project
|
name: Add labelled issues to tech debt project
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
contains(github.event.issue.labels.*.name, 'A-Developer-Experience') ||
|
contains(github.event.issue.labels.*.name, 'A-Developer-Experience') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Documentation') ||
|
contains(github.event.issue.labels.*.name, 'A-Documentation') ||
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
add_design_pr_to_project:
|
add_design_pr_to_project:
|
||||||
name: Move PRs asking for design review to the design board
|
name: Move PRs asking for design review to the design board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@v2.x
|
||||||
id: find_team_members
|
id: find_team_members
|
||||||
@@ -73,7 +73,7 @@ jobs:
|
|||||||
|
|
||||||
add_product_pr_to_project:
|
add_product_pr_to_project:
|
||||||
name: Move PRs asking for design review to the design board
|
name: Move PRs asking for design review to the design board
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: octokit/graphql-action@v2.x
|
- uses: octokit/graphql-action@v2.x
|
||||||
id: find_team_members
|
id: find_team_members
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ on:
|
|||||||
- cron: "30 1 * * *"
|
- cron: "30 1 * * *"
|
||||||
jobs:
|
jobs:
|
||||||
close:
|
close:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
issues: write
|
issues: write
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
Move_Unabeled_Issue_On_Project_Board:
|
Move_Unabeled_Issue_On_Project_Board:
|
||||||
name: Move no longer X-Needs-Info issues to Triaged
|
name: Move no longer X-Needs-Info issues to Triaged
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
${{
|
${{
|
||||||
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
!contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
|
||||||
@@ -44,7 +44,7 @@ jobs:
|
|||||||
|
|
||||||
remove_Z-Labs_label:
|
remove_Z-Labs_label:
|
||||||
name: Remove Z-Labs label when features behind labs flags are removed
|
name: Remove Z-Labs label when features behind labs flags are removed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
if: >
|
if: >
|
||||||
!(contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
!(contains(github.event.issue.labels.*.name, 'A-Maths') ||
|
||||||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ on:
|
|||||||
- cron: "0 3 * * 0" # 3am every Sunday
|
- cron: "0 3 * * 0" # 3am every Sunday
|
||||||
jobs:
|
jobs:
|
||||||
update:
|
update:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ concurrency: ${{ github.workflow }}
|
|||||||
jobs:
|
jobs:
|
||||||
bot:
|
bot:
|
||||||
name: Release topic update
|
name: Release topic update
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
environment: Matrix
|
environment: Matrix
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v7
|
- uses: actions/github-script@v7
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
"test:playwright:open": "yarn test:playwright --ui",
|
"test:playwright:open": "yarn test:playwright --ui",
|
||||||
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
|
"test:playwright:screenshots": "yarn test:playwright:screenshots:build && yarn test:playwright:screenshots:run",
|
||||||
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
|
"test:playwright:screenshots:build": "docker build playwright -t element-web-playwright",
|
||||||
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright",
|
"test:playwright:screenshots:run": "docker run --rm --network host -e BASE_URL -e CI -v $(pwd):/work/ -v $(node -e 'console.log(require(`path`).dirname(require.resolve(`matrix-js-sdk/package.json`)))'):/work/node_modules/matrix-js-sdk -v /var/run/docker.sock:/var/run/docker.sock -v /tmp/:/tmp/ -it element-web-playwright",
|
||||||
"coverage": "yarn test --coverage",
|
"coverage": "yarn test --coverage",
|
||||||
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
|
"analyse:unused-exports": "ts-node ./scripts/analyse_unused_exports.ts",
|
||||||
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
"analyse:webpack-bundles": "webpack-bundle-analyzer webpack-stats.json webapp",
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"@types/react-dom": "17.0.25",
|
"@types/react-dom": "17.0.25",
|
||||||
"@types/react": "17.0.83",
|
"@types/react": "17.0.83",
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/seedrandom": "3.0.8",
|
||||||
"oidc-client-ts": "3.0.1",
|
"oidc-client-ts": "3.1.0",
|
||||||
"jwt-decode": "4.0.0",
|
"jwt-decode": "4.0.0",
|
||||||
"@vector-im/compound-design-tokens": "1.8.0",
|
"@vector-im/compound-design-tokens": "1.8.0",
|
||||||
"@vector-im/compound-web": "7.0.0",
|
"@vector-im/compound-web": "7.0.0",
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
"emojibase-regex": "15.3.2",
|
"emojibase-regex": "15.3.2",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"filesize": "10.1.4",
|
"filesize": "10.1.6",
|
||||||
"github-markdown-css": "^5.5.1",
|
"github-markdown-css": "^5.5.1",
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
"highlight.js": "^11.3.1",
|
"highlight.js": "^11.3.1",
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
"png-chunks-extract": "^1.0.0",
|
"png-chunks-extract": "^1.0.0",
|
||||||
"posthog-js": "1.157.2",
|
"posthog-js": "1.157.2",
|
||||||
"qrcode": "1.5.4",
|
"qrcode": "1.5.4",
|
||||||
"re-resizable": "^6.9.0",
|
"re-resizable": "6.9.17",
|
||||||
"react": "17.0.2",
|
"react": "17.0.2",
|
||||||
"react-beautiful-dnd": "^13.1.0",
|
"react-beautiful-dnd": "^13.1.0",
|
||||||
"react-blurhash": "^0.3.0",
|
"react-blurhash": "^0.3.0",
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
"react-transition-group": "^4.4.1",
|
"react-transition-group": "^4.4.1",
|
||||||
"rfc4648": "^1.4.0",
|
"rfc4648": "^1.4.0",
|
||||||
"sanitize-filename": "^1.6.3",
|
"sanitize-filename": "^1.6.3",
|
||||||
"sanitize-html": "2.13.0",
|
"sanitize-html": "2.13.1",
|
||||||
"tar-js": "^0.3.0",
|
"tar-js": "^0.3.0",
|
||||||
"temporal-polyfill": "^0.2.5",
|
"temporal-polyfill": "^0.2.5",
|
||||||
"ua-parser-js": "^1.0.2",
|
"ua-parser-js": "^1.0.2",
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
"@playwright/test": "^1.40.1",
|
"@playwright/test": "^1.40.1",
|
||||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||||
"@sentry/webpack-plugin": "^2.7.1",
|
"@sentry/webpack-plugin": "^2.7.1",
|
||||||
|
"@stylistic/eslint-plugin": "^2.9.0",
|
||||||
"@svgr/webpack": "^8.0.0",
|
"@svgr/webpack": "^8.0.0",
|
||||||
"@testing-library/dom": "^9.0.0",
|
"@testing-library/dom": "^9.0.0",
|
||||||
"@testing-library/jest-dom": "^6.0.0",
|
"@testing-library/jest-dom": "^6.0.0",
|
||||||
@@ -223,8 +224,8 @@
|
|||||||
"@types/tar-js": "^0.3.5",
|
"@types/tar-js": "^0.3.5",
|
||||||
"@types/ua-parser-js": "^0.7.36",
|
"@types/ua-parser-js": "^0.7.36",
|
||||||
"@types/uuid": "^10.0.0",
|
"@types/uuid": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||||
"@typescript-eslint/parser": "^7.0.0",
|
"@typescript-eslint/parser": "^8.0.0",
|
||||||
"axe-core": "4.10.0",
|
"axe-core": "4.10.0",
|
||||||
"babel-jest": "^29.0.0",
|
"babel-jest": "^29.0.0",
|
||||||
"babel-loader": "^9.0.0",
|
"babel-loader": "^9.0.0",
|
||||||
@@ -246,7 +247,7 @@
|
|||||||
"eslint-plugin-import": "^2.25.4",
|
"eslint-plugin-import": "^2.25.4",
|
||||||
"eslint-plugin-jest": "^28.0.0",
|
"eslint-plugin-jest": "^28.0.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-matrix-org": "1.2.1",
|
"eslint-plugin-matrix-org": "^2.0.2",
|
||||||
"eslint-plugin-react": "^7.28.0",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"eslint-plugin-unicorn": "^56.0.0",
|
"eslint-plugin-unicorn": "^56.0.0",
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ export default defineConfig({
|
|||||||
command: process.env.CI ? "npx serve -p 8080 -L ./webapp" : "yarn start",
|
command: process.env.CI ? "npx serve -p 8080 -L ./webapp" : "yarn start",
|
||||||
url: `${baseURL}/config.json`,
|
url: `${baseURL}/config.json`,
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
|
timeout: (process.env.CI ? 30 : 120) * 1000,
|
||||||
},
|
},
|
||||||
testDir: "playwright/e2e",
|
testDir: "playwright/e2e",
|
||||||
outputDir: "playwright/test-results",
|
outputDir: "playwright/test-results",
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ test.describe("Landmark navigation tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("without any rooms", async ({ page, homeserver, app, user }) => {
|
test("without any rooms", async ({ page, homeserver, app, user }) => {
|
||||||
|
// sometimes the space button doesn't appear right away
|
||||||
|
await expect(page.locator(".mx_SpaceButton_active")).toBeVisible();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Without any rooms, there is no tile in the roomlist to be focused.
|
* Without any rooms, there is no tile in the roomlist to be focused.
|
||||||
* So the next landmark in the list should be focused instead.
|
* So the next landmark in the list should be focused instead.
|
||||||
|
|||||||
@@ -96,7 +96,10 @@ test.describe("HTML Export", () => {
|
|||||||
|
|
||||||
// Send a bunch of messages to populate the room
|
// Send a bunch of messages to populate the room
|
||||||
for (let i = 1; i < 10; i++) {
|
for (let i = 1; i < 10; i++) {
|
||||||
await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
|
const respone = await app.client.sendMessage(room.roomId, { body: `Testing ${i}`, msgtype: "m.text" });
|
||||||
|
if (i == 1) {
|
||||||
|
await app.client.reactToMessage(room.roomId, null, respone.event_id, "🙃");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for all the messages to be displayed
|
// Wait for all the messages to be displayed
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ test.describe("Login", () => {
|
|||||||
const h1 = await page.getByRole("heading", { name: "Verify this device", level: 1 });
|
const h1 = await page.getByRole("heading", { name: "Verify this device", level: 1 });
|
||||||
await expect(h1).toBeVisible();
|
await expect(h1).toBeVisible();
|
||||||
|
|
||||||
expect(h1.locator(".mx_CompleteSecurity_skip")).not.toBeVisible();
|
await expect(h1.locator(".mx_CompleteSecurity_skip")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export class MessageBuilder {
|
|||||||
/**
|
/**
|
||||||
* Map of message content -> event.
|
* Map of message content -> event.
|
||||||
*/
|
*/
|
||||||
messages = new Map<String, Promise<JSHandle<MatrixEvent>>>();
|
messages = new Map<string, Promise<JSHandle<MatrixEvent>>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to find a MatrixEvent by its body content
|
* Utility to find a MatrixEvent by its body content
|
||||||
@@ -222,14 +222,7 @@ export class MessageBuilder {
|
|||||||
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
|
threadId: !ev.isThreadRoot ? ev.threadRootId : undefined,
|
||||||
}));
|
}));
|
||||||
const roomId = await room.evaluate((room) => room.roomId);
|
const roomId = await room.evaluate((room) => room.roomId);
|
||||||
|
await bot.reactToMessage(roomId, threadId, id, reaction);
|
||||||
await bot.sendEvent(roomId, threadId ?? null, "m.reaction", {
|
|
||||||
"m.relates_to": {
|
|
||||||
rel_type: "m.annotation",
|
|
||||||
event_id: id,
|
|
||||||
key: reaction,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
})(this);
|
})(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,4 +140,51 @@ test.describe("Account user settings tab", () => {
|
|||||||
await expect(page.locator(".mx_UserMenu .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice
|
await expect(page.locator(".mx_UserMenu .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice
|
||||||
await expect(page.locator(".mx_RoomView_wrapper .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice
|
await expect(page.locator(".mx_RoomView_wrapper .mx_BaseAvatar").getByText("A")).toBeVisible(); // Alice
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ported to a playwright test because the jest test was very flakey for no obvious reason
|
||||||
|
test("should display an error if the code is incorrect when adding a phone number", async ({ uut, page }) => {
|
||||||
|
const dummyUrl = "https://nowhere.dummy/_matrix/client/unstable/add_threepid/msisdn/submit_token";
|
||||||
|
|
||||||
|
await page.route(
|
||||||
|
`**/_matrix/client/v3/account/3pid/msisdn/requestToken`,
|
||||||
|
async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
json: {
|
||||||
|
success: true,
|
||||||
|
sid: "1",
|
||||||
|
msisdn: "447700900000",
|
||||||
|
intl_fmt: "+44 7700 900000",
|
||||||
|
submit_url: dummyUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ times: 1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
await page.route(
|
||||||
|
dummyUrl,
|
||||||
|
async (route) => {
|
||||||
|
await route.fulfill({
|
||||||
|
status: 400,
|
||||||
|
json: {
|
||||||
|
errcode: "M_THREEPID_AUTH_FAILED",
|
||||||
|
error: "That code is definitely wrong",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ times: 1 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const phoneSection = page.getByTestId("mx_AccountPhoneNumbers");
|
||||||
|
await phoneSection.getByRole("textbox", { name: "Phone Number" }).fill("07700900000");
|
||||||
|
await phoneSection.getByRole("button", { name: "Add" }).click();
|
||||||
|
|
||||||
|
await phoneSection
|
||||||
|
.getByRole("textbox", { name: "Verification code" })
|
||||||
|
.fill("A small eurasian field mouse dancing the paso doble");
|
||||||
|
|
||||||
|
await phoneSection.getByRole("button", { name: "Continue" }).click();
|
||||||
|
|
||||||
|
await expect(page.getByRole("heading", { name: "Unable to verify phone number." })).toBeVisible();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ test.describe("Appearance user settings tab", () => {
|
|||||||
await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-added.png");
|
await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-added.png");
|
||||||
|
|
||||||
await util.removeCustomTheme();
|
await util.removeCustomTheme();
|
||||||
await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme.png");
|
await expect(util.getThemePanel()).toMatchScreenshot("theme-panel-custom-theme-removed.png");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ export class MessageBuilder {
|
|||||||
/**
|
/**
|
||||||
* Map of message content -> event.
|
* Map of message content -> event.
|
||||||
*/
|
*/
|
||||||
messages = new Map<String, Promise<JSHandle<MatrixEvent>>>();
|
messages = new Map<string, Promise<JSHandle<MatrixEvent>>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility to find a MatrixEvent by its body content
|
* Utility to find a MatrixEvent by its body content
|
||||||
|
|||||||
@@ -143,6 +143,29 @@ export class Client {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a reaction to to a message
|
||||||
|
* @param roomId ID of the room to send the reaction into
|
||||||
|
* @param threadId ID of the thread to send into or null for main timeline
|
||||||
|
* @param eventId Event ID of the message you are reacting to
|
||||||
|
* @param reaction The reaction text to send
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async reactToMessage(
|
||||||
|
roomId: string,
|
||||||
|
threadId: string | null,
|
||||||
|
eventId: string,
|
||||||
|
reaction: string,
|
||||||
|
): Promise<ISendEventResponse> {
|
||||||
|
return this.sendEvent(roomId, threadId ?? null, "m.reaction", {
|
||||||
|
"m.relates_to": {
|
||||||
|
rel_type: "m.annotation",
|
||||||
|
event_id: eventId,
|
||||||
|
key: reaction,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a room with given options.
|
* Create a room with given options.
|
||||||
* @param options the options to apply when creating the room
|
* @param options the options to apply when creating the room
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { randB64Bytes } from "../../utils/rand";
|
|||||||
// Docker tag to use for synapse docker image.
|
// Docker tag to use for synapse docker image.
|
||||||
// We target a specific digest as every now and then a Synapse update will break our CI.
|
// We target a specific digest as every now and then a Synapse update will break our CI.
|
||||||
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
// This digest is updated by the playwright-image-updates.yaml workflow periodically.
|
||||||
const DOCKER_TAG = "develop@sha256:fd6ba2d8471a0807e1bccef4124b22d17f0058f2cf9285066fdd94d8c631964a";
|
const DOCKER_TAG = "develop@sha256:47c62aa9507a24820190eef547861c0d278cc83fe90329c46b9f4329eed88ef4";
|
||||||
|
|
||||||
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
async function cfgDirFromTemplate(opts: StartHomeserverOpts): Promise<Omit<HomeserverConfig, "dockerUrl">> {
|
||||||
const templateDir = path.join(__dirname, "templates", opts.template);
|
const templateDir = path.join(__dirname, "templates", opts.template);
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
@@ -99,10 +99,10 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
&[aria-checked="true"]::before {
|
&[aria-checked="true"]::before {
|
||||||
content: "";
|
content: "";
|
||||||
width: 12px;
|
width: 16px;
|
||||||
height: 12px;
|
height: 16px;
|
||||||
margin-left: -20px;
|
margin-left: -22px;
|
||||||
margin-right: 8px;
|
margin-right: 6px;
|
||||||
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/check.svg");
|
||||||
mask-size: 100%;
|
mask-size: 100%;
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ $button-gap: 24px;
|
|||||||
|
|
||||||
.mx_ImageView_button_more::before {
|
.mx_ImageView_button_more::before {
|
||||||
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
|
||||||
|
mask-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ImageView_button_close {
|
.mx_ImageView_button_close {
|
||||||
|
|||||||
@@ -122,4 +122,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
--MessageActionBar-icon-size: 12px;
|
--MessageActionBar-icon-size: 12px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MessageActionBar_optionsButton {
|
||||||
|
--MessageActionBar-icon-size: 22px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -278,6 +278,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
.mx_MessageComposer_buttonMenu::before {
|
.mx_MessageComposer_buttonMenu::before {
|
||||||
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
|
mask-image: url("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg");
|
||||||
|
mask-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageComposer_sendMessage {
|
.mx_MessageComposer_sendMessage {
|
||||||
@@ -295,7 +296,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
top: 8px;
|
top: 8px;
|
||||||
left: 9px;
|
left: 9px;
|
||||||
|
|
||||||
mask-image: url("@vector-im/compound-design-tokens/icons/send-solid.svg");
|
mask-image: url("$(res)/img/element-icons/send-message.svg");
|
||||||
mask-repeat: no-repeat;
|
mask-repeat: no-repeat;
|
||||||
mask-size: contain;
|
mask-size: contain;
|
||||||
mask-position: center;
|
mask-position: center;
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ export type Defaultize<P, D> = P extends any
|
|||||||
Partial<Pick<D, Exclude<keyof D, keyof P>>>
|
Partial<Pick<D, Exclude<keyof D, keyof P>>>
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||||
export type DeepReadonly<T> = T extends (infer R)[]
|
export type DeepReadonly<T> = T extends (infer R)[]
|
||||||
? DeepReadonlyArray<R>
|
? DeepReadonlyArray<R>
|
||||||
: T extends Function
|
: T extends Function
|
||||||
@@ -42,6 +43,7 @@ export type DeepReadonly<T> = T extends (infer R)[]
|
|||||||
: T extends object
|
: T extends object
|
||||||
? DeepReadonlyObject<T>
|
? DeepReadonlyObject<T>
|
||||||
: T;
|
: T;
|
||||||
|
/* eslint-enable @typescript-eslint/no-unsafe-function-type */
|
||||||
|
|
||||||
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
|
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
|
||||||
|
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ export default abstract class BasePlatform {
|
|||||||
try {
|
try {
|
||||||
const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)!);
|
const [version, deferUntil] = JSON.parse(localStorage.getItem(UPDATE_DEFER_KEY)!);
|
||||||
return newVersion !== version || Date.now() > deferUntil;
|
return newVersion !== version || Date.now() > deferUntil;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -380,7 +380,7 @@ export default abstract class BasePlatform {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await idbSave("pickleKey", [userId, deviceId], data);
|
await idbSave("pickleKey", [userId, deviceId], data);
|
||||||
} catch (e) {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return encodeUnpaddedBase64(randomArray);
|
return encodeUnpaddedBase64(randomArray);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ function shouldCleanupDrafts(): boolean {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return Date.now() > parsedTimestamp + DRAFT_CLEANUP_PERIOD;
|
return Date.now() > parsedTimestamp + DRAFT_CLEANUP_PERIOD;
|
||||||
} catch (error) {
|
} catch {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const EMOJI_REGEX = (() => {
|
|||||||
// want the app to completely crash on older platforms. We use the
|
// want the app to completely crash on older platforms. We use the
|
||||||
// constructor here to avoid a syntax error on such platforms.
|
// constructor here to avoid a syntax error on such platforms.
|
||||||
return new RegExp("\\p{RGI_Emoji}(?!\\uFE0E)(?:(?<!\\uFE0F)\\uFE0F)?|[\\u{1f1e6}-\\u{1f1ff}]", "v");
|
return new RegExp("\\p{RGI_Emoji}(?!\\uFE0E)(?:(?<!\\uFE0F)\\uFE0F)?|[\\u{1f1e6}-\\u{1f1ff}]", "v");
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// v mode not supported; fall back to matching nothing
|
// v mode not supported; fall back to matching nothing
|
||||||
return /(?!)/;
|
return /(?!)/;
|
||||||
}
|
}
|
||||||
@@ -61,7 +61,7 @@ export const EMOJI_REGEX = (() => {
|
|||||||
const BIGEMOJI_REGEX = (() => {
|
const BIGEMOJI_REGEX = (() => {
|
||||||
try {
|
try {
|
||||||
return new RegExp(`^(${EMOJI_REGEX.source})+$`, "iv");
|
return new RegExp(`^(${EMOJI_REGEX.source})+$`, "iv");
|
||||||
} catch (_e) {
|
} catch {
|
||||||
// Fall back, just like for EMOJI_REGEX
|
// Fall back, just like for EMOJI_REGEX
|
||||||
return /(?!)/;
|
return /(?!)/;
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ export function isUrlPermitted(inputUrl: string): boolean {
|
|||||||
try {
|
try {
|
||||||
// URL parser protocol includes the trailing colon
|
// URL parser protocol includes the trailing colon
|
||||||
return PERMITTED_URL_SCHEMES.includes(new URL(inputUrl).protocol.slice(0, -1));
|
return PERMITTED_URL_SCHEMES.includes(new URL(inputUrl).protocol.slice(0, -1));
|
||||||
} catch (e) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -827,7 +827,7 @@ export default class LegacyCallHandler extends EventEmitter {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
this.addCallForRoom(roomId, call);
|
this.addCallForRoom(roomId, call);
|
||||||
} catch (e) {
|
} catch {
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
title: _t("voip|already_in_call"),
|
title: _t("voip|already_in_call"),
|
||||||
description: _t("voip|already_in_call_person"),
|
description: _t("voip|already_in_call_person"),
|
||||||
|
|||||||
@@ -211,7 +211,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
||||||
const diff = Date.now() - registrationTime;
|
const diff = Date.now() - registrationTime;
|
||||||
return diff / 36e5 <= hours;
|
return diff / 36e5 <= hours;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,7 +220,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||||||
try {
|
try {
|
||||||
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time")!, 10);
|
||||||
return timestamp.getTime() <= registrationTime;
|
return timestamp.getTime() <= registrationTime;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ export class PosthogAnalytics {
|
|||||||
let appVersion: string | undefined;
|
let appVersion: string | undefined;
|
||||||
try {
|
try {
|
||||||
appVersion = await platform?.getAppVersion();
|
appVersion = await platform?.getAppVersion();
|
||||||
} catch (e) {
|
} catch {
|
||||||
// this happens if no version is set i.e. in dev
|
// this happens if no version is set i.e. in dev
|
||||||
appVersion = "unknown";
|
appVersion = "unknown";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Presence {
|
|||||||
try {
|
try {
|
||||||
await this.unavailableTimer.finished();
|
await this.unavailableTimer.finished();
|
||||||
this.setState(SetPresence.Unavailable);
|
this.setState(SetPresence.Unavailable);
|
||||||
} catch (e) {
|
} catch {
|
||||||
/* aborted, stop got called */
|
/* aborted, stop got called */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function getRoomNotifsState(client: MatrixClient, roomId: string): RoomNo
|
|||||||
let roomRule: IPushRule | undefined;
|
let roomRule: IPushRule | undefined;
|
||||||
try {
|
try {
|
||||||
roomRule = client.getRoomPushRule("global", roomId);
|
roomRule = client.getRoomPushRule("global", roomId);
|
||||||
} catch (err) {
|
} catch {
|
||||||
// Possible that the client doesn't have pushRules yet. If so, it
|
// Possible that the client doesn't have pushRules yet. If so, it
|
||||||
// hasn't started either, so indicate that this room is not notifying.
|
// hasn't started either, so indicate that this room is not notifying.
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -855,14 +855,14 @@ const onMessage = function (event: MessageEvent<any>): void {
|
|||||||
try {
|
try {
|
||||||
if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager()?.uiUrl;
|
if (!openManagerUrl) openManagerUrl = IntegrationManagers.sharedInstance().getPrimaryManager()?.uiUrl;
|
||||||
configUrl = new URL(openManagerUrl!);
|
configUrl = new URL(openManagerUrl!);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// No integrations UI URL, ignore silently.
|
// No integrations UI URL, ignore silently.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let eventOriginUrl: URL;
|
let eventOriginUrl: URL;
|
||||||
try {
|
try {
|
||||||
eventOriginUrl = new URL(event.origin);
|
eventOriginUrl = new URL(event.origin);
|
||||||
} catch (e) {
|
} catch {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO -- Scalar postMessage API should be namespaced with event.data.api field
|
// TODO -- Scalar postMessage API should be namespaced with event.data.api field
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
|
|||||||
// be preferred over their config.
|
// be preferred over their config.
|
||||||
desktopBuilds: {
|
desktopBuilds: {
|
||||||
available: true,
|
available: true,
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
logo: require("../res/img/element-desktop-logo.svg").default,
|
logo: require("../res/img/element-desktop-logo.svg").default,
|
||||||
url: "https://element.io/get-started",
|
url: "https://element.io/get-started",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -727,7 +727,7 @@ export const Commands = [
|
|||||||
isEnabled: (cli) => !isCurrentLocalRoom(cli),
|
isEnabled: (cli) => !isCurrentLocalRoom(cli),
|
||||||
runFn: function (cli, roomId) {
|
runFn: function (cli, roomId) {
|
||||||
try {
|
try {
|
||||||
cli.forceDiscardSession(roomId);
|
cli.getCrypto()?.forceDiscardSession(roomId);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return reject(e instanceof Error ? e.message : e);
|
return reject(e instanceof Error ? e.message : e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ export class SlidingSyncManager {
|
|||||||
try {
|
try {
|
||||||
// wait until the next sync before returning as RoomView may need to know the current state
|
// wait until the next sync before returning as RoomView may need to know the current state
|
||||||
await p;
|
await p;
|
||||||
} catch (err) {
|
} catch {
|
||||||
logger.warn("SlidingSync setRoomVisible:", roomId, visible, "failed to confirm transaction");
|
logger.warn("SlidingSync setRoomVisible:", roomId, visible, "failed to confirm transaction");
|
||||||
}
|
}
|
||||||
return roomId;
|
return roomId;
|
||||||
@@ -305,7 +305,7 @@ export class SlidingSyncManager {
|
|||||||
} else {
|
} else {
|
||||||
await this.slidingSync!.setListRanges(SlidingSyncManager.ListSearch, ranges);
|
await this.slidingSync!.setListRanges(SlidingSyncManager.ListSearch, ranges);
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch {
|
||||||
// do nothing, as we reject only when we get interrupted but that's fine as the next
|
// do nothing, as we reject only when we get interrupted but that's fine as the next
|
||||||
// request will include our data
|
// request will include our data
|
||||||
} finally {
|
} finally {
|
||||||
@@ -357,7 +357,7 @@ export class SlidingSyncManager {
|
|||||||
}
|
}
|
||||||
const clientWellKnown = await AutoDiscovery.findClientConfig(clientDomain);
|
const clientWellKnown = await AutoDiscovery.findClientConfig(clientDomain);
|
||||||
proxyUrl = clientWellKnown?.["org.matrix.msc3575.proxy"]?.url;
|
proxyUrl = clientWellKnown?.["org.matrix.msc3575.proxy"]?.url;
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Either client.getDomain() is null so we've shorted out, or is invalid so `AutoDiscovery.findClientConfig` has thrown
|
// Either client.getDomain() is null so we've shorted out, or is invalid so `AutoDiscovery.findClientConfig` has thrown
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ export default class UserActivity {
|
|||||||
attachedTimers.forEach((t) => t.start());
|
attachedTimers.forEach((t) => t.start());
|
||||||
try {
|
try {
|
||||||
await timeout.finished();
|
await timeout.finished();
|
||||||
} catch (_e) {
|
} catch {
|
||||||
/* aborted */
|
/* aborted */
|
||||||
}
|
}
|
||||||
attachedTimers.forEach((t) => t.abort());
|
attachedTimers.forEach((t) => t.abort());
|
||||||
|
|||||||
@@ -7,10 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
|
|||||||
Please see LICENSE files in the repository root for full details.
|
Please see LICENSE files in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, { JSX, useEffect, useState } from "react";
|
||||||
import { KeyBackupInfo } from "matrix-js-sdk/src/crypto-api";
|
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../../MatrixClientPeg";
|
|
||||||
import dis from "../../../../dispatcher/dispatcher";
|
import dis from "../../../../dispatcher/dispatcher";
|
||||||
import { _t } from "../../../../languageHandler";
|
import { _t } from "../../../../languageHandler";
|
||||||
import Modal from "../../../../Modal";
|
import Modal from "../../../../Modal";
|
||||||
@@ -18,81 +16,73 @@ import RestoreKeyBackupDialog from "../../../../components/views/dialogs/securit
|
|||||||
import { Action } from "../../../../dispatcher/actions";
|
import { Action } from "../../../../dispatcher/actions";
|
||||||
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
import DialogButtons from "../../../../components/views/elements/DialogButtons";
|
||||||
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
import BaseDialog from "../../../../components/views/dialogs/BaseDialog";
|
||||||
|
import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext.tsx";
|
||||||
|
|
||||||
interface IProps {
|
/**
|
||||||
newVersionInfo: KeyBackupInfo;
|
* Properties for {@link NewRecoveryMethodDialog}.
|
||||||
|
*/
|
||||||
|
interface NewRecoveryMethodDialogProps {
|
||||||
|
/**
|
||||||
|
* Callback when the dialog is dismissed.
|
||||||
|
*/
|
||||||
onFinished(): void;
|
onFinished(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class NewRecoveryMethodDialog extends React.PureComponent<IProps> {
|
// Export as default instead of a named export so that it can be dynamically imported with `Modal.createDialogAsync`
|
||||||
private onOkClick = (): void => {
|
|
||||||
this.props.onFinished();
|
|
||||||
};
|
|
||||||
|
|
||||||
private onGoToSettingsClick = (): void => {
|
/**
|
||||||
this.props.onFinished();
|
* Dialog to inform the user that a new recovery method has been detected.
|
||||||
dis.fire(Action.ViewUserSettings);
|
*/
|
||||||
};
|
export default function NewRecoveryMethodDialog({ onFinished }: NewRecoveryMethodDialogProps): JSX.Element {
|
||||||
|
const matrixClient = useMatrixClientContext();
|
||||||
|
const [isKeyBackupEnabled, setIsKeyBackupEnabled] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
const checkBackupEnabled = async (): Promise<void> => {
|
||||||
|
const crypto = matrixClient.getCrypto();
|
||||||
|
setIsKeyBackupEnabled(Boolean(crypto && (await crypto.getActiveSessionBackupVersion()) !== null));
|
||||||
|
};
|
||||||
|
|
||||||
private onSetupClick = async (): Promise<void> => {
|
checkBackupEnabled();
|
||||||
Modal.createDialog(
|
}, [matrixClient]);
|
||||||
RestoreKeyBackupDialog,
|
|
||||||
{
|
|
||||||
onFinished: this.props.onFinished,
|
|
||||||
},
|
|
||||||
undefined,
|
|
||||||
/* priority = */ false,
|
|
||||||
/* static = */ true,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
public render(): React.ReactNode {
|
function onClick(): void {
|
||||||
const title = (
|
if (isKeyBackupEnabled) {
|
||||||
<span className="mx_KeyBackupFailedDialog_title">
|
onFinished();
|
||||||
{_t("encryption|new_recovery_method_detected|title")}
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
|
|
||||||
const newMethodDetected = <p>{_t("encryption|new_recovery_method_detected|description_1")}</p>;
|
|
||||||
|
|
||||||
const hackWarning = (
|
|
||||||
<strong className="warning">{_t("encryption|new_recovery_method_detected|warning")}</strong>
|
|
||||||
);
|
|
||||||
|
|
||||||
let content: JSX.Element | undefined;
|
|
||||||
if (MatrixClientPeg.safeGet().getKeyBackupEnabled()) {
|
|
||||||
content = (
|
|
||||||
<div>
|
|
||||||
{newMethodDetected}
|
|
||||||
<p>{_t("encryption|new_recovery_method_detected|description_2")}</p>
|
|
||||||
{hackWarning}
|
|
||||||
<DialogButtons
|
|
||||||
primaryButton={_t("action|ok")}
|
|
||||||
onPrimaryButtonClick={this.onOkClick}
|
|
||||||
cancelButton={_t("common|go_to_settings")}
|
|
||||||
onCancel={this.onGoToSettingsClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
content = (
|
Modal.createDialog(
|
||||||
<div>
|
RestoreKeyBackupDialog,
|
||||||
{newMethodDetected}
|
{
|
||||||
{hackWarning}
|
onFinished,
|
||||||
<DialogButtons
|
},
|
||||||
primaryButton={_t("common|setup_secure_messages")}
|
undefined,
|
||||||
onPrimaryButtonClick={this.onSetupClick}
|
false,
|
||||||
cancelButton={_t("common|go_to_settings")}
|
true,
|
||||||
onCancel={this.onGoToSettingsClick}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
|
||||||
<BaseDialog className="mx_KeyBackupFailedDialog" onFinished={this.props.onFinished} title={title}>
|
|
||||||
{content}
|
|
||||||
</BaseDialog>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BaseDialog
|
||||||
|
className="mx_KeyBackupFailedDialog"
|
||||||
|
onFinished={onFinished}
|
||||||
|
title={
|
||||||
|
<span className="mx_KeyBackupFailedDialog_title">
|
||||||
|
{_t("encryption|new_recovery_method_detected|title")}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<p>{_t("encryption|new_recovery_method_detected|description_1")}</p>
|
||||||
|
{isKeyBackupEnabled && <p>{_t("encryption|new_recovery_method_detected|description_2")}</p>}
|
||||||
|
<strong className="warning">{_t("encryption|new_recovery_method_detected|warning")}</strong>
|
||||||
|
<DialogButtons
|
||||||
|
primaryButton={_t("common|setup_secure_messages")}
|
||||||
|
onPrimaryButtonClick={onClick}
|
||||||
|
cancelButton={_t("common|go_to_settings")}
|
||||||
|
onCancel={() => {
|
||||||
|
onFinished();
|
||||||
|
dis.fire(Action.ViewUserSettings);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import React, { useEffect, useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
|
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
|
import UploadBigSvg from "../../../res/img/upload-big.svg";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
parent: HTMLElement | null;
|
parent: HTMLElement | null;
|
||||||
@@ -106,11 +107,7 @@ const FileDropTarget: React.FC<IProps> = ({ parent, onFileDrop }) => {
|
|||||||
if (state.dragging) {
|
if (state.dragging) {
|
||||||
return (
|
return (
|
||||||
<div className="mx_FileDropTarget">
|
<div className="mx_FileDropTarget">
|
||||||
<img
|
<img src={UploadBigSvg} className="mx_FileDropTarget_image" alt="" />
|
||||||
src={require("../../../res/img/upload-big.svg").default}
|
|
||||||
className="mx_FileDropTarget_image"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
{_t("room|drop_file_prompt")}
|
{_t("room|drop_file_prompt")}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ type IProps<T> = WithKeyFunction<T> & {
|
|||||||
AdditionalOptions?: FunctionComponent<AdditionalOptionsProps>;
|
AdditionalOptions?: FunctionComponent<AdditionalOptionsProps>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function calculateKey<T>(value: T, toKey: ((key: T) => Key) | undefined): Key {
|
||||||
|
return toKey ? toKey(value) : (value as Key);
|
||||||
|
}
|
||||||
|
|
||||||
export function GenericDropdownMenu<T>({
|
export function GenericDropdownMenu<T>({
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
@@ -119,23 +123,24 @@ export function GenericDropdownMenu<T>({
|
|||||||
}: IProps<T>): JSX.Element {
|
}: IProps<T>): JSX.Element {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu<HTMLElement>();
|
||||||
|
|
||||||
|
const valueKey = calculateKey(value, toKey);
|
||||||
const selected: GenericDropdownMenuItem<T> | undefined = options
|
const selected: GenericDropdownMenuItem<T> | undefined = options
|
||||||
.flatMap((it) => (isGenericDropdownMenuGroup(it) ? [it, ...it.options] : [it]))
|
.flatMap((it) => (isGenericDropdownMenuGroup(it) ? [it, ...it.options] : [it]))
|
||||||
.find((option) => (toKey ? toKey(option.key) === toKey(value) : option.key === value));
|
.find((option) => calculateKey(option.key, toKey) === valueKey);
|
||||||
let contextMenuOptions: JSX.Element;
|
let contextMenuOptions: JSX.Element;
|
||||||
if (options && isGenericDropdownMenuGroupArray(options)) {
|
if (options && isGenericDropdownMenuGroupArray(options)) {
|
||||||
contextMenuOptions = (
|
contextMenuOptions = (
|
||||||
<>
|
<>
|
||||||
{options.map((group) => (
|
{options.map((group) => (
|
||||||
<GenericDropdownMenuGroup
|
<GenericDropdownMenuGroup
|
||||||
key={toKey?.(group.key) ?? (group.key as Key)}
|
key={calculateKey(group.key, toKey)}
|
||||||
label={group.label}
|
label={group.label}
|
||||||
description={group.description}
|
description={group.description}
|
||||||
adornment={group.adornment}
|
adornment={group.adornment}
|
||||||
>
|
>
|
||||||
{group.options.map((option) => (
|
{group.options.map((option) => (
|
||||||
<GenericDropdownMenuOption
|
<GenericDropdownMenuOption
|
||||||
key={toKey?.(option.key) ?? (option.key as Key)}
|
key={calculateKey(option.key, toKey)}
|
||||||
label={option.label}
|
label={option.label}
|
||||||
description={option.description}
|
description={option.description}
|
||||||
onClick={(ev: ButtonEvent) => {
|
onClick={(ev: ButtonEvent) => {
|
||||||
@@ -144,7 +149,7 @@ export function GenericDropdownMenu<T>({
|
|||||||
onClose?.(ev);
|
onClose?.(ev);
|
||||||
}}
|
}}
|
||||||
adornment={option.adornment}
|
adornment={option.adornment}
|
||||||
isSelected={option === selected}
|
isSelected={calculateKey(option.key, toKey) === valueKey}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</GenericDropdownMenuGroup>
|
</GenericDropdownMenuGroup>
|
||||||
@@ -156,7 +161,7 @@ export function GenericDropdownMenu<T>({
|
|||||||
<>
|
<>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<GenericDropdownMenuOption
|
<GenericDropdownMenuOption
|
||||||
key={toKey?.(option.key) ?? (option.key as Key)}
|
key={calculateKey(option.key, toKey)}
|
||||||
label={option.label}
|
label={option.label}
|
||||||
description={option.description}
|
description={option.description}
|
||||||
onClick={(ev: ButtonEvent) => {
|
onClick={(ev: ButtonEvent) => {
|
||||||
@@ -165,7 +170,7 @@ export function GenericDropdownMenu<T>({
|
|||||||
onClose?.(ev);
|
onClose?.(ev);
|
||||||
}}
|
}}
|
||||||
adornment={option.adornment}
|
adornment={option.adornment}
|
||||||
isSelected={option === selected}
|
isSelected={calculateKey(option.key, toKey) === valueKey}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -161,9 +161,11 @@ export default class LegacyCallEventGrouper extends EventEmitter {
|
|||||||
|
|
||||||
public toggleSilenced = (): void => {
|
public toggleSilenced = (): void => {
|
||||||
const silenced = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
const silenced = LegacyCallHandler.instance.isCallSilenced(this.callId);
|
||||||
silenced
|
if (silenced) {
|
||||||
? LegacyCallHandler.instance.unSilenceCall(this.callId)
|
LegacyCallHandler.instance.unSilenceCall(this.callId);
|
||||||
: LegacyCallHandler.instance.silenceCall(this.callId);
|
} else {
|
||||||
|
LegacyCallHandler.instance.silenceCall(this.callId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private setCallListeners(): void {
|
private setCallListeners(): void {
|
||||||
|
|||||||
@@ -982,7 +982,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
try {
|
try {
|
||||||
const config = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(params.hs_url);
|
const config = await AutoDiscoveryUtils.validateServerConfigWithStaticUrls(params.hs_url);
|
||||||
newState.serverConfig = config;
|
newState.serverConfig = config;
|
||||||
} catch (err) {
|
} catch {
|
||||||
logger.warn("Failed to load hs_url param:", params.hs_url);
|
logger.warn("Failed to load hs_url param:", params.hs_url);
|
||||||
}
|
}
|
||||||
} else if (params.client_secret && params.session_id && params.hs_url && params.is_url && params.sid) {
|
} else if (params.client_secret && params.session_id && params.hs_url && params.is_url && params.sid) {
|
||||||
@@ -1631,8 +1631,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise<void> => {
|
cli.on(CryptoEvent.KeyBackupFailed, async (errcode): Promise<void> => {
|
||||||
let haveNewVersion: boolean | undefined;
|
let haveNewVersion: boolean | undefined;
|
||||||
let newVersionInfo: KeyBackupInfo | null = null;
|
let newVersionInfo: KeyBackupInfo | null = null;
|
||||||
|
const keyBackupEnabled = Boolean(
|
||||||
|
cli.getCrypto() && (await cli.getCrypto()?.getActiveSessionBackupVersion()) !== null,
|
||||||
|
);
|
||||||
|
|
||||||
// if key backup is still enabled, there must be a new backup in place
|
// if key backup is still enabled, there must be a new backup in place
|
||||||
if (cli.getKeyBackupEnabled()) {
|
if (keyBackupEnabled) {
|
||||||
haveNewVersion = true;
|
haveNewVersion = true;
|
||||||
} else {
|
} else {
|
||||||
// otherwise check the server to see if there's a new one
|
// otherwise check the server to see if there's a new one
|
||||||
@@ -1650,7 +1654,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||||||
import(
|
import(
|
||||||
"../../async-components/views/dialogs/security/NewRecoveryMethodDialog"
|
"../../async-components/views/dialogs/security/NewRecoveryMethodDialog"
|
||||||
) as unknown as Promise<typeof NewRecoveryMethodDialog>,
|
) as unknown as Promise<typeof NewRecoveryMethodDialog>,
|
||||||
{ newVersionInfo: newVersionInfo! },
|
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
Modal.createDialogAsync(
|
Modal.createDialogAsync(
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
SyncState,
|
SyncState,
|
||||||
SyncStateData,
|
SyncStateData,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import WarningIcon from "@vector-im/compound-design-tokens/assets/web/icons/warning";
|
import { WarningIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import { _t, _td } from "../../languageHandler";
|
import { _t, _td } from "../../languageHandler";
|
||||||
import Resend from "../../Resend";
|
import Resend from "../../Resend";
|
||||||
|
|||||||
@@ -691,7 +691,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
|
|||||||
|
|
||||||
hierarchy.removeRelation(parentId, childId);
|
hierarchy.removeRelation(parentId, childId);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
setError(_t("space|failed_remove_rooms"));
|
setError(_t("space|failed_remove_rooms"));
|
||||||
}
|
}
|
||||||
setRemoving(false);
|
setRemoving(false);
|
||||||
@@ -724,7 +724,7 @@ const ManageButtons: React.FC<IManageButtonsProps> = ({ hierarchy, selected, set
|
|||||||
// mutate the local state to save us having to refetch the world
|
// mutate the local state to save us having to refetch the world
|
||||||
existingContent.suggested = content.suggested;
|
existingContent.suggested = content.suggested;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
setError("Failed to update some suggestions. Try again later");
|
setError("Failed to update some suggestions. Try again later");
|
||||||
}
|
}
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
|||||||
@@ -973,7 +973,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
UserActivity.sharedInstance().timeWhileActiveRecently(this.readMarkerActivityTimer);
|
UserActivity.sharedInstance().timeWhileActiveRecently(this.readMarkerActivityTimer);
|
||||||
try {
|
try {
|
||||||
await this.readMarkerActivityTimer.finished();
|
await this.readMarkerActivityTimer.finished();
|
||||||
} catch (e) {
|
} catch {
|
||||||
continue; /* aborted */
|
continue; /* aborted */
|
||||||
}
|
}
|
||||||
// outside of try/catch to not swallow errors
|
// outside of try/catch to not swallow errors
|
||||||
@@ -988,7 +988,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
|
|||||||
UserActivity.sharedInstance().timeWhileActiveNow(this.readReceiptActivityTimer);
|
UserActivity.sharedInstance().timeWhileActiveNow(this.readReceiptActivityTimer);
|
||||||
try {
|
try {
|
||||||
await this.readReceiptActivityTimer.finished();
|
await this.readReceiptActivityTimer.finished();
|
||||||
} catch (e) {
|
} catch {
|
||||||
continue; /* aborted */
|
continue; /* aborted */
|
||||||
}
|
}
|
||||||
// outside of try/catch to not swallow errors
|
// outside of try/catch to not swallow errors
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import { Icon as LiveIcon } from "../../../res/img/compound/live-8px.svg";
|
|||||||
import { VoiceBroadcastRecording, VoiceBroadcastRecordingsStoreEvent } from "../../voice-broadcast";
|
import { VoiceBroadcastRecording, VoiceBroadcastRecordingsStoreEvent } from "../../voice-broadcast";
|
||||||
import { SDKContext } from "../../contexts/SDKContext";
|
import { SDKContext } from "../../contexts/SDKContext";
|
||||||
import { shouldShowFeedback } from "../../utils/Feedback";
|
import { shouldShowFeedback } from "../../utils/Feedback";
|
||||||
|
import DarkLightModeSvg from "../../../res/img/element-icons/roomlist/dark-light-mode.svg";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
isPanelCollapsed: boolean;
|
isPanelCollapsed: boolean;
|
||||||
@@ -414,12 +415,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||||||
: _t("user_menu|switch_theme_dark")
|
: _t("user_menu|switch_theme_dark")
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<img
|
<img src={DarkLightModeSvg} role="presentation" alt="" width={16} />
|
||||||
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg").default}
|
|
||||||
role="presentation"
|
|
||||||
alt=""
|
|
||||||
width={16}
|
|
||||||
/>
|
|
||||||
</RovingAccessibleButton>
|
</RovingAccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
{topSection}
|
{topSection}
|
||||||
|
|||||||
@@ -274,7 +274,7 @@ export default class ForgotPassword extends React.Component<Props, State> {
|
|||||||
await this.reset.setNewPassword(this.state.password);
|
await this.reset.setNewPassword(this.state.password);
|
||||||
this.setState({ phase: Phase.Done });
|
this.setState({ phase: Phase.Done });
|
||||||
modal.close();
|
modal.close();
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Email not confirmed, yet. Retry after a while.
|
// Email not confirmed, yet. Retry after a while.
|
||||||
await sleep(emailCheckInterval);
|
await sleep(emailCheckInterval);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// We're importing via require specifically so the svg becomes a URI rather than a DOM element.
|
// We're importing via require specifically so the svg becomes a URI rather than a DOM element.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
const matrixSvg = require("../../../res/img/matrix.svg").default;
|
const matrixSvg = require("../../../res/img/matrix.svg").default;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ interface IProps extends Omit<ComponentProps<BaseAvatarType>, "name" | "url" | "
|
|||||||
}
|
}
|
||||||
|
|
||||||
const WidgetAvatar: React.FC<IProps> = ({ app, className, size = "20px", ...props }) => {
|
const WidgetAvatar: React.FC<IProps> = ({ app, className, size = "20px", ...props }) => {
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
let iconUrls = [require("../../../../res/img/element-icons/room/default_app.svg").default];
|
let iconUrls = [require("../../../../res/img/element-icons/room/default_app.svg").default];
|
||||||
// heuristics for some better icons until Widgets support their own icons
|
// heuristics for some better icons until Widgets support their own icons
|
||||||
if (app.type.includes("jitsi")) {
|
if (app.type.includes("jitsi")) {
|
||||||
@@ -31,6 +32,7 @@ const WidgetAvatar: React.FC<IProps> = ({ app, className, size = "20px", ...prop
|
|||||||
} else if (app.type.includes("clock")) {
|
} else if (app.type.includes("clock")) {
|
||||||
iconUrls = [require("../../../../res/img/element-icons/room/default_clock.svg").default];
|
iconUrls = [require("../../../../res/img/element-icons/room/default_clock.svg").default];
|
||||||
}
|
}
|
||||||
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseAvatar
|
<BaseAvatar
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import LazyRenderList from "../elements/LazyRenderList";
|
|||||||
import { useSettingValue } from "../../../hooks/useSettings";
|
import { useSettingValue } from "../../../hooks/useSettings";
|
||||||
import { filterBoolean } from "../../../utils/arrays";
|
import { filterBoolean } from "../../../utils/arrays";
|
||||||
import { NonEmptyArray } from "../../../@types/common";
|
import { NonEmptyArray } from "../../../@types/common";
|
||||||
|
import WarningBadgeSvg from "../../../../res/img/element-icons/warning-badge.svg";
|
||||||
|
|
||||||
// These values match CSS
|
// These values match CSS
|
||||||
const ROW_HEIGHT = 32 + 12;
|
const ROW_HEIGHT = 32 + 12;
|
||||||
@@ -228,12 +229,7 @@ export const AddExistingToSpace: React.FC<IAddExistingToSpaceProps> = ({
|
|||||||
if (error) {
|
if (error) {
|
||||||
footer = (
|
footer = (
|
||||||
<>
|
<>
|
||||||
<img
|
<img src={WarningBadgeSvg} height="24" width="24" alt="" />
|
||||||
src={require("../../../../res/img/element-icons/warning-badge.svg").default}
|
|
||||||
height="24"
|
|
||||||
width="24"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
|
|
||||||
<span className="mx_AddExistingToSpaceDialog_error">
|
<span className="mx_AddExistingToSpaceDialog_error">
|
||||||
<div className="mx_AddExistingToSpaceDialog_errorHeading">
|
<div className="mx_AddExistingToSpaceDialog_errorHeading">
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import Spinner from "../elements/Spinner";
|
|||||||
import Heading from "../typography/Heading";
|
import Heading from "../typography/Heading";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
newVersion: string;
|
newVersion: DevelopVersionString;
|
||||||
version: string;
|
version: DevelopVersionString;
|
||||||
onFinished: (success: boolean) => void;
|
onFinished: (success: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +32,28 @@ interface Commit {
|
|||||||
|
|
||||||
const REPOS = ["element-hq/element-web", "matrix-org/matrix-js-sdk"] as const;
|
const REPOS = ["element-hq/element-web", "matrix-org/matrix-js-sdk"] as const;
|
||||||
|
|
||||||
|
export type DevelopVersionString = string & { _developVersionString: never };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Parse a version string is compatible with the Changelog dialog ([element-version]-js-[js-sdk-version])
|
||||||
|
*/
|
||||||
|
export function parseVersion(version: string): Record<(typeof REPOS)[number], string> | null {
|
||||||
|
const parts = version.split("-");
|
||||||
|
if (parts.length === 3 && parts[1] === "js") {
|
||||||
|
const obj: Record<string, string> = {};
|
||||||
|
for (let i = 0; i < REPOS.length; i++) {
|
||||||
|
const commit = parts[2 * i];
|
||||||
|
obj[REPOS[i]] = commit;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkVersion(version: string): version is DevelopVersionString {
|
||||||
|
return parseVersion(version) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
export default class ChangelogDialog extends React.Component<IProps, State> {
|
export default class ChangelogDialog extends React.Component<IProps, State> {
|
||||||
public constructor(props: IProps) {
|
public constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -58,14 +80,11 @@ export default class ChangelogDialog extends React.Component<IProps, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public componentDidMount(): void {
|
public componentDidMount(): void {
|
||||||
const version = this.props.newVersion.split("-");
|
const commits = parseVersion(this.props.version)!;
|
||||||
const version2 = this.props.version.split("-");
|
const newCommits = parseVersion(this.props.newVersion)!;
|
||||||
if (version == null || version2 == null) return;
|
|
||||||
// parse versions of form: [vectorversion]-react-[react-sdk-version]-js-[js-sdk-version]
|
for (const repo of REPOS) {
|
||||||
for (let i = 0; i < REPOS.length; i++) {
|
this.fetchChanges(repo, commits[repo], newCommits[repo]);
|
||||||
const oldVersion = version2[2 * i];
|
|
||||||
const newVersion = version[2 * i];
|
|
||||||
this.fetchChanges(REPOS[i], oldVersion, newVersion);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ import {
|
|||||||
TimelineEvents,
|
TimelineEvents,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import OverflowHorizontalSvg from "@vector-im/compound-design-tokens/icons/overflow-horizontal.svg";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
@@ -104,7 +106,7 @@ const Entry: React.FC<IEntryProps<any>> = ({ room, type, content, matrixClient:
|
|||||||
try {
|
try {
|
||||||
await cli.sendEvent(room.roomId, type, content);
|
await cli.sendEvent(room.roomId, type, content);
|
||||||
setSendState(SendState.Sent);
|
setSendState(SendState.Sent);
|
||||||
} catch (e) {
|
} catch {
|
||||||
setSendState(SendState.Failed);
|
setSendState(SendState.Failed);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -278,13 +280,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
|
|||||||
return (
|
return (
|
||||||
<EntityTile
|
<EntityTile
|
||||||
className="mx_EntityTile_ellipsis"
|
className="mx_EntityTile_ellipsis"
|
||||||
avatarJsx={
|
avatarJsx={<BaseAvatar url={OverflowHorizontalSvg} name="..." size="36px" />}
|
||||||
<BaseAvatar
|
|
||||||
url={require("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg").default}
|
|
||||||
name="..."
|
|
||||||
size="36px"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
name={text}
|
name={text}
|
||||||
showPresence={false}
|
showPresence={false}
|
||||||
onClick={() => setTruncateAt(totalCount)}
|
onClick={() => setTruncateAt(totalCount)}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import { arrayFastClone } from "../../../utils/arrays";
|
|||||||
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";
|
import { ElementWidget } from "../../../stores/widgets/StopGapWidget";
|
||||||
import { ELEMENT_CLIENT_ID } from "../../../identifiers";
|
import { ELEMENT_CLIENT_ID } from "../../../identifiers";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
import WarningBadgeSvg from "../../../../res/img/element-icons/warning-badge.svg";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
widgetDefinition: IModalWidgetOpenRequestData;
|
widgetDefinition: IModalWidgetOpenRequestData;
|
||||||
@@ -185,12 +186,7 @@ export default class ModalWidgetDialog extends React.PureComponent<IProps, IStat
|
|||||||
onFinished={this.props.onFinished}
|
onFinished={this.props.onFinished}
|
||||||
>
|
>
|
||||||
<div className="mx_ModalWidgetDialog_warning">
|
<div className="mx_ModalWidgetDialog_warning">
|
||||||
<img
|
<img src={WarningBadgeSvg} height="16" width="16" alt="" />
|
||||||
src={require("../../../../res/img/element-icons/warning-badge.svg").default}
|
|
||||||
height="16"
|
|
||||||
width="16"
|
|
||||||
alt=""
|
|
||||||
/>
|
|
||||||
{_t("widget|modal_data_warning", {
|
{_t("widget|modal_data_warning", {
|
||||||
widgetDomain: parsed.hostname,
|
widgetDomain: parsed.hostname,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ import BaseDialog from "./BaseDialog";
|
|||||||
import CopyableText from "../elements/CopyableText";
|
import CopyableText from "../elements/CopyableText";
|
||||||
import { XOR } from "../../../@types/common";
|
import { XOR } from "../../../@types/common";
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
const socials = [
|
const socials = [
|
||||||
{
|
{
|
||||||
name: "Facebook",
|
name: "Facebook",
|
||||||
img: require("../../../../res/img/social/facebook.png"),
|
img: require("../../../../res/img/social/facebook.png"),
|
||||||
url: (url: String) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
|
url: (url: string) => `https://www.facebook.com/sharer/sharer.php?u=${url}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Twitter",
|
name: "Twitter",
|
||||||
@@ -52,6 +53,7 @@ const socials = [
|
|||||||
url: (url: string) => `mailto:?body=${url}`,
|
url: (url: string) => `mailto:?body=${url}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||||
|
|
||||||
interface BaseProps {
|
interface BaseProps {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export default class AccessSecretStorageDialog extends React.PureComponent<IProp
|
|||||||
recoveryKeyValid: true,
|
recoveryKeyValid: true,
|
||||||
recoveryKeyCorrect: correct,
|
recoveryKeyCorrect: correct,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch {
|
||||||
this.setState({
|
this.setState({
|
||||||
recoveryKeyValid: false,
|
recoveryKeyValid: false,
|
||||||
recoveryKeyCorrect: false,
|
recoveryKeyCorrect: false,
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export default class RestoreKeyBackupDialog extends React.PureComponent<IProps,
|
|||||||
try {
|
try {
|
||||||
decodeRecoveryKey(recoveryKey);
|
decodeRecoveryKey(recoveryKey);
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ import { SetupEncryptionStore, Phase } from "../../../../stores/SetupEncryptionS
|
|||||||
|
|
||||||
function iconFromPhase(phase?: Phase): string {
|
function iconFromPhase(phase?: Phase): string {
|
||||||
if (phase === Phase.Done) {
|
if (phase === Phase.Done) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
return require("../../../../../res/img/e2e/verified-deprecated.svg").default;
|
return require("../../../../../res/img/e2e/verified-deprecated.svg").default;
|
||||||
} else {
|
} else {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
return require("../../../../../res/img/e2e/warning-deprecated.svg").default;
|
return require("../../../../../res/img/e2e/warning-deprecated.svg").default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
|
import WarningSvg from "../../../../res/img/warning.svg";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
errorMsg?: string;
|
errorMsg?: string;
|
||||||
}
|
}
|
||||||
@@ -16,7 +18,7 @@ const AppWarning: React.FC<IProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<div className="mx_AppWarning">
|
<div className="mx_AppWarning">
|
||||||
<div>
|
<div>
|
||||||
<img src={require("../../../../res/img/warning.svg").default} alt="" />
|
<img src={WarningSvg} alt="" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<span>{props.errorMsg || "Error"}</span>
|
<span>{props.errorMsg || "Error"}</span>
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import React, { ReactElement } from "react";
|
|||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
import { Room, RoomMember } from "matrix-js-sdk/src/matrix";
|
||||||
import { Tooltip } from "@vector-im/compound-web";
|
import { Tooltip } from "@vector-im/compound-web";
|
||||||
import UserIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-profile-solid";
|
import { LinkIcon, UserSolidIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import LinkIcon from "@vector-im/compound-design-tokens/assets/web/icons/link";
|
|
||||||
|
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
@@ -63,7 +62,7 @@ const PillMemberAvatar: React.FC<{
|
|||||||
if (member) {
|
if (member) {
|
||||||
return <MemberAvatar member={member} size="16px" aria-hidden="true" hideTitle />;
|
return <MemberAvatar member={member} size="16px" aria-hidden="true" hideTitle />;
|
||||||
}
|
}
|
||||||
return <UserIcon className="mx_Pill_UserIcon mx_BaseAvatar" />;
|
return <UserSolidIcon className="mx_Pill_UserIcon mx_BaseAvatar" />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface PillProps {
|
export interface PillProps {
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||||||
const inReplyToEventId = getParentEventId(ev);
|
const inReplyToEventId = getParentEventId(ev);
|
||||||
if (!inReplyToEventId) return null;
|
if (!inReplyToEventId) return null;
|
||||||
return await this.getEvent(inReplyToEventId);
|
return await this.getEvent(inReplyToEventId);
|
||||||
} catch (e) {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,7 +154,7 @@ export default class ReplyChain extends React.Component<IProps, IState> {
|
|||||||
// ask the client to fetch the event we want using the context API, only interface to do so is to ask
|
// ask the client to fetch the event we want using the context API, only interface to do so is to ask
|
||||||
// for a timeline with that event, but once it is loaded we can use findEventById to look up the ev map
|
// for a timeline with that event, but once it is loaded we can use findEventById to look up the ev map
|
||||||
await this.matrixClient.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
|
await this.matrixClient.getEventTimeline(this.room.getUnfilteredTimelineSet(), eventId);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// if it fails catch the error and return early, there's no point trying to find the event in this case.
|
// if it fails catch the error and return early, there's no point trying to find the event in this case.
|
||||||
// Return null as it is falsy and thus should be treated as an error (as the event cannot be resolved).
|
// Return null as it is falsy and thus should be treated as an error (as the event cannot be resolved).
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ interface ISSOButtonProps extends IProps {
|
|||||||
|
|
||||||
const getIcon = (brand: IdentityProviderBrand | string): string | null => {
|
const getIcon = (brand: IdentityProviderBrand | string): string | null => {
|
||||||
switch (brand) {
|
switch (brand) {
|
||||||
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||||
case IdentityProviderBrand.Apple:
|
case IdentityProviderBrand.Apple:
|
||||||
return require(`../../../../res/img/element-icons/brands/apple.svg`).default;
|
return require(`../../../../res/img/element-icons/brands/apple.svg`).default;
|
||||||
case IdentityProviderBrand.Facebook:
|
case IdentityProviderBrand.Facebook:
|
||||||
@@ -47,6 +48,7 @@ const getIcon = (brand: IdentityProviderBrand | string): string | null => {
|
|||||||
return require(`../../../../res/img/element-icons/brands/twitter.svg`).default;
|
return require(`../../../../res/img/element-icons/brands/twitter.svg`).default;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
|
/* eslint-enable @typescript-eslint/no-require-imports */
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ const useMapWithStyle = ({
|
|||||||
throw new Error("Invalid geo URI");
|
throw new Error("Invalid geo URI");
|
||||||
}
|
}
|
||||||
map.setCenter({ lon: coords.longitude, lat: coords.latitude });
|
map.setCenter({ lon: coords.longitude, lat: coords.latitude });
|
||||||
} catch (_error) {
|
} catch (e) {
|
||||||
logger.error("Could not set map center");
|
logger.error("Could not set map center", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [map, centerGeoUri]);
|
}, [map, centerGeoUri]);
|
||||||
@@ -80,8 +80,8 @@ const useMapWithStyle = ({
|
|||||||
[bounds.east, bounds.north],
|
[bounds.east, bounds.north],
|
||||||
);
|
);
|
||||||
map.fitBounds(lngLatBounds, { padding: 100, maxZoom: 15 });
|
map.fitBounds(lngLatBounds, { padding: 100, maxZoom: 15 });
|
||||||
} catch (_error) {
|
} catch (e) {
|
||||||
logger.error("Invalid map bounds");
|
logger.error("Invalid map bounds", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [map, bounds]);
|
}, [map, bounds]);
|
||||||
@@ -170,7 +170,7 @@ const MapComponent: React.FC<MapProps> = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick && onClick();
|
onClick?.();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export default class LegacyCallEvent extends React.PureComponent<IProps, IState>
|
|||||||
this.props.callEventGrouper.addListener(LegacyCallEventGrouperEvent.LengthChanged, this.onLengthChanged);
|
this.props.callEventGrouper.addListener(LegacyCallEventGrouperEvent.LengthChanged, this.onLengthChanged);
|
||||||
|
|
||||||
this.resizeObserver = new ResizeObserver(this.resizeObserverCallback);
|
this.resizeObserver = new ResizeObserver(this.resizeObserverCallback);
|
||||||
this.wrapperElement.current && this.resizeObserver.observe(this.wrapperElement.current);
|
if (this.wrapperElement.current) this.resizeObserver.observe(this.wrapperElement.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount(): void {
|
public componentWillUnmount(): void {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export let DOWNLOAD_ICON_URL: string; // cached copy of the download.svg asset f
|
|||||||
|
|
||||||
async function cacheDownloadIcon(): Promise<void> {
|
async function cacheDownloadIcon(): Promise<void> {
|
||||||
if (DOWNLOAD_ICON_URL) return; // cached already
|
if (DOWNLOAD_ICON_URL) return; // cached already
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
const svg = await fetch(require("@vector-im/compound-design-tokens/icons/download.svg").default).then((r) =>
|
const svg = await fetch(require("@vector-im/compound-design-tokens/icons/download.svg").default).then((r) =>
|
||||||
r.text(),
|
r.text(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { MediaEventContent } from "matrix-js-sdk/src/types";
|
|||||||
|
|
||||||
import MImageBody from "./MImageBody";
|
import MImageBody from "./MImageBody";
|
||||||
import { BLURHASH_FIELD } from "../../../utils/image-media";
|
import { BLURHASH_FIELD } from "../../../utils/image-media";
|
||||||
|
import IconsShowStickersSvg from "../../../../res/img/icons-show-stickers.svg";
|
||||||
|
|
||||||
export default class MStickerBody extends MImageBody {
|
export default class MStickerBody extends MImageBody {
|
||||||
// Mostly empty to prevent default behaviour of MImageBody
|
// Mostly empty to prevent default behaviour of MImageBody
|
||||||
@@ -44,7 +45,7 @@ export default class MStickerBody extends MImageBody {
|
|||||||
aria-hidden
|
aria-hidden
|
||||||
alt=""
|
alt=""
|
||||||
className="mx_MStickerBody_placeholder"
|
className="mx_MStickerBody_placeholder"
|
||||||
src={require("../../../../res/img/icons-show-stickers.svg").default}
|
src={IconsShowStickersSvg}
|
||||||
width="80"
|
width="80"
|
||||||
height="80"
|
height="80"
|
||||||
onMouseEnter={this.onImageEnter}
|
onMouseEnter={this.onImageEnter}
|
||||||
|
|||||||
@@ -21,16 +21,18 @@ import {
|
|||||||
EventType,
|
EventType,
|
||||||
} from "matrix-js-sdk/src/matrix";
|
} from "matrix-js-sdk/src/matrix";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import PinIcon from "@vector-im/compound-design-tokens/assets/web/icons/pin";
|
import {
|
||||||
import UnpinIcon from "@vector-im/compound-design-tokens/assets/web/icons/unpin";
|
PinIcon,
|
||||||
import ThreadIcon from "@vector-im/compound-design-tokens/assets/web/icons/threads";
|
UnpinIcon,
|
||||||
import ReplyIcon from "@vector-im/compound-design-tokens/assets/web/icons/reply";
|
OverflowHorizontalIcon,
|
||||||
import ContextMenuIcon from "@vector-im/compound-design-tokens/assets/web/icons/overflow-horizontal";
|
ReplyIcon,
|
||||||
import ResendIcon from "@vector-im/compound-design-tokens/assets/web/icons/restart";
|
DeleteIcon,
|
||||||
import TrashcanIcon from "@vector-im/compound-design-tokens/assets/web/icons/delete";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import { Icon as EditIcon } from "../../../../res/img/element-icons/room/message-bar/edit.svg";
|
import { Icon as EditIcon } from "../../../../res/img/element-icons/room/message-bar/edit.svg";
|
||||||
import { Icon as EmojiIcon } from "../../../../res/img/element-icons/room/message-bar/emoji.svg";
|
import { Icon as EmojiIcon } from "../../../../res/img/element-icons/room/message-bar/emoji.svg";
|
||||||
|
import { Icon as ResendIcon } from "../../../../res/img/element-icons/retry.svg";
|
||||||
|
import { Icon as ThreadIcon } from "../../../../res/img/element-icons/message/thread.svg";
|
||||||
import { Icon as ExpandMessageIcon } from "../../../../res/img/element-icons/expand-message.svg";
|
import { Icon as ExpandMessageIcon } from "../../../../res/img/element-icons/expand-message.svg";
|
||||||
import { Icon as CollapseMessageIcon } from "../../../../res/img/element-icons/collapse-message.svg";
|
import { Icon as CollapseMessageIcon } from "../../../../res/img/element-icons/collapse-message.svg";
|
||||||
import type { Relations } from "matrix-js-sdk/src/matrix";
|
import type { Relations } from "matrix-js-sdk/src/matrix";
|
||||||
@@ -130,7 +132,7 @@ const OptionsButton: React.FC<IOptionsButtonProps> = ({
|
|||||||
tabIndex={isActive ? 0 : -1}
|
tabIndex={isActive ? 0 : -1}
|
||||||
placement="left"
|
placement="left"
|
||||||
>
|
>
|
||||||
<ContextMenuIcon />
|
<OverflowHorizontalIcon />
|
||||||
</ContextMenuTooltipButton>
|
</ContextMenuTooltipButton>
|
||||||
{contextMenu}
|
{contextMenu}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@@ -454,7 +456,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
|
|||||||
key="cancel"
|
key="cancel"
|
||||||
placement="left"
|
placement="left"
|
||||||
>
|
>
|
||||||
<TrashcanIcon />
|
<DeleteIcon />
|
||||||
</RovingAccessibleButton>
|
</RovingAccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePha
|
|||||||
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
|
||||||
import ErrorDialog from "../dialogs/ErrorDialog";
|
import ErrorDialog from "../dialogs/ErrorDialog";
|
||||||
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
import { useMatrixClientContext } from "../../../contexts/MatrixClientContext";
|
||||||
|
import WarningDeprecatedSvg from "../../../../res/img/e2e/warning-deprecated.svg";
|
||||||
|
import WarningSvg from "../../../../res/img/e2e/warning.svg";
|
||||||
|
|
||||||
// cancellation codes which constitute a key mismatch
|
// cancellation codes which constitute a key mismatch
|
||||||
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
|
const MISMATCHES = ["m.key_mismatch", "m.user_error", "m.mismatched_sas"];
|
||||||
@@ -79,7 +81,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
) {
|
) {
|
||||||
isShowingMismatchModal.current = true;
|
isShowingMismatchModal.current = true;
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
headerImage: require("../../../../res/img/e2e/warning-deprecated.svg").default,
|
headerImage: WarningDeprecatedSvg,
|
||||||
title: _t("encryption|messages_not_secure|title"),
|
title: _t("encryption|messages_not_secure|title"),
|
||||||
description: (
|
description: (
|
||||||
<div>
|
<div>
|
||||||
@@ -118,7 +120,7 @@ const EncryptionPanel: React.FC<IProps> = (props: IProps) => {
|
|||||||
setRequesting(false);
|
setRequesting(false);
|
||||||
|
|
||||||
Modal.createDialog(ErrorDialog, {
|
Modal.createDialog(ErrorDialog, {
|
||||||
headerImage: require("../../../../res/img/e2e/warning.svg").default,
|
headerImage: WarningSvg,
|
||||||
title: _t("encryption|verification|error_starting_title"),
|
title: _t("encryption|verification|error_starting_title"),
|
||||||
description: _t("encryption|verification|error_starting_description"),
|
description: _t("encryption|verification|error_starting_description"),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1356,7 +1356,7 @@ export const useDevices = (userId: string): IDevice[] | undefined | null => {
|
|||||||
|
|
||||||
disambiguateDevices(devices);
|
disambiguateDevices(devices);
|
||||||
setDevices(devices);
|
setDevices(devices);
|
||||||
} catch (err) {
|
} catch {
|
||||||
setDevices(null);
|
setDevices(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import { KnownMembership } from "matrix-js-sdk/src/types";
|
|||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
import { Button, Tooltip } from "@vector-im/compound-web";
|
import { Button, Tooltip } from "@vector-im/compound-web";
|
||||||
import UserAddIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add-solid";
|
import UserAddIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add-solid";
|
||||||
|
// eslint-disable-next-line no-restricted-imports
|
||||||
|
import OverflowHorizontalSvg from "@vector-im/compound-design-tokens/icons/overflow-horizontal.svg";
|
||||||
|
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
@@ -237,13 +239,7 @@ export default class MemberList extends React.Component<IProps, IState> {
|
|||||||
return (
|
return (
|
||||||
<EntityTile
|
<EntityTile
|
||||||
className="mx_EntityTile_ellipsis"
|
className="mx_EntityTile_ellipsis"
|
||||||
avatarJsx={
|
avatarJsx={<BaseAvatar url={OverflowHorizontalSvg} name="..." size="36px" />}
|
||||||
<BaseAvatar
|
|
||||||
url={require("@vector-im/compound-design-tokens/icons/overflow-horizontal.svg").default}
|
|
||||||
name="..."
|
|
||||||
size="36px"
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
name={text}
|
name={text}
|
||||||
showPresence={false}
|
showPresence={false}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import { VoiceBroadcastInfoState } from "../../../voice-broadcast";
|
|||||||
import { createCantStartVoiceMessageBroadcastDialog } from "../dialogs/CantStartVoiceMessageBroadcastDialog";
|
import { createCantStartVoiceMessageBroadcastDialog } from "../dialogs/CantStartVoiceMessageBroadcastDialog";
|
||||||
import { UIFeature } from "../../../settings/UIFeature";
|
import { UIFeature } from "../../../settings/UIFeature";
|
||||||
import { formatTimeLeft } from "../../../DateUtils";
|
import { formatTimeLeft } from "../../../DateUtils";
|
||||||
|
import RoomReplacedSvg from "../../../../res/img/room_replaced.svg";
|
||||||
|
|
||||||
// The prefix used when persisting editor drafts to localstorage.
|
// The prefix used when persisting editor drafts to localstorage.
|
||||||
export const WYSIWYG_EDITOR_STATE_STORAGE_PREFIX = "mx_wysiwyg_state_";
|
export const WYSIWYG_EDITOR_STATE_STORAGE_PREFIX = "mx_wysiwyg_state_";
|
||||||
@@ -619,7 +620,7 @@ export class MessageComposer extends React.Component<IProps, IState> {
|
|||||||
aria-hidden
|
aria-hidden
|
||||||
alt=""
|
alt=""
|
||||||
className="mx_MessageComposer_roomReplaced_icon"
|
className="mx_MessageComposer_roomReplaced_icon"
|
||||||
src={require("../../../../res/img/room_replaced.svg").default}
|
src={RoomReplacedSvg}
|
||||||
/>
|
/>
|
||||||
<span className="mx_MessageComposer_roomReplaced_header">
|
<span className="mx_MessageComposer_roomReplaced_header">
|
||||||
{_t("composer|room_upgraded_notice")}
|
{_t("composer|room_upgraded_notice")}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const RoomInfoLine: FC<IProps> = ({ room }) => {
|
|||||||
if (room.getMyMembership() !== KnownMembership.Invite) return null;
|
if (room.getMyMembership() !== KnownMembership.Invite) return null;
|
||||||
try {
|
try {
|
||||||
return await room.client.getRoomSummary(room.roomId);
|
return await room.client.getRoomSummary(room.roomId);
|
||||||
} catch (e) {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}, [room]);
|
}, [room]);
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import { EventTimeline, JoinRule, MatrixError, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
import { EventTimeline, JoinRule, MatrixError, Room, RoomStateEvent } from "matrix-js-sdk/src/matrix";
|
||||||
import { KnownMembership } from "matrix-js-sdk/src/types";
|
import { KnownMembership } from "matrix-js-sdk/src/types";
|
||||||
import React, { ReactElement, ReactNode, useCallback, useState, VFC } from "react";
|
import React, { ReactElement, ReactNode, useCallback, useState, VFC } from "react";
|
||||||
import CloseIcon from "@vector-im/compound-design-tokens/assets/web/icons/close";
|
import { CloseIcon, CheckIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import CheckIcon from "@vector-im/compound-design-tokens/assets/web/icons/check";
|
|
||||||
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import { useTypedEventEmitterState } from "../../../hooks/useEventEmitter";
|
import { useTypedEventEmitterState } from "../../../hooks/useEventEmitter";
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import ThreadIcon from "@vector-im/compound-design-tokens/assets/web/icons/threads-solid";
|
import { ThreadsIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
|
|
||||||
import { MessagePreview } from "../../../stores/room-list/MessagePreviewStore";
|
import { MessagePreview } from "../../../stores/room-list/MessagePreviewStore";
|
||||||
import { Call } from "../../../models/Call";
|
import { Call } from "../../../models/Call";
|
||||||
@@ -49,7 +49,7 @@ export const RoomTileSubtitle: React.FC<Props> = ({
|
|||||||
"mx_RoomTile_subtitle--thread-reply": messagePreview.isThreadReply,
|
"mx_RoomTile_subtitle--thread-reply": messagePreview.isThreadReply,
|
||||||
});
|
});
|
||||||
|
|
||||||
const icon = messagePreview.isThreadReply ? <ThreadIcon className="mx_Icon mx_Icon_12" /> : null;
|
const icon = messagePreview.isThreadReply ? <ThreadsIcon className="mx_Icon mx_Icon_12" /> : null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} id={messagePreviewId(roomId)} title={messagePreview.text}>
|
<div className={className} id={messagePreviewId(roomId)} title={messagePreview.text}>
|
||||||
|
|||||||
@@ -202,11 +202,13 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private defaultStickerpickerContent(): JSX.Element {
|
private defaultStickerpickerContent(): JSX.Element {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
|
const imgSrc = require("../../../../res/img/stickerpack-placeholder.png");
|
||||||
return (
|
return (
|
||||||
<AccessibleButton onClick={this.launchManageIntegrations} className="mx_Stickers_contentPlaceholder">
|
<AccessibleButton onClick={this.launchManageIntegrations} className="mx_Stickers_contentPlaceholder">
|
||||||
<p>{_t("stickers|empty")}</p>
|
<p>{_t("stickers|empty")}</p>
|
||||||
<p className="mx_Stickers_addLink">{_t("stickers|empty_add_prompt")}</p>
|
<p className="mx_Stickers_addLink">{_t("stickers|empty_add_prompt")}</p>
|
||||||
<img src={require("../../../../res/img/stickerpack-placeholder.png")} alt="" />
|
<img src={imgSrc} alt="" />
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||