Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f72e09173b | |||
| b49a29f3df | |||
| 09f56ed3b6 | |||
| 7b396e8de7 | |||
| 3c87309f7d | |||
| 6339bcda15 | |||
| 68070b2e97 | |||
| ee5d2609df | |||
| d18aa31d7d | |||
| 35babd83d6 | |||
| cace9d918f | |||
| 3e88689d69 | |||
| 3b4027846d | |||
| b54e4e3b98 | |||
| d791e3fe8a | |||
| 394356c4df | |||
| 772a443486 | |||
| 09bbf796dc | |||
| 86692ce0a7 | |||
| 0ca4f8013b | |||
| a73335168d | |||
| 635b0e6fe2 | |||
| 8fe2e72245 | |||
| 40a322ac05 | |||
| 5f92215ead | |||
| c02db4ebb8 | |||
| d38eb4fdb4 | |||
| 9fa8b34ebe | |||
| a1939f69ee | |||
| ba1b76da39 | |||
| 6d99678ade | |||
| 652b9f5b5b | |||
| 3bd14239b1 | |||
| 35afc2fdf8 | |||
| 78b40a6fed | |||
| 2e0adc5832 | |||
| 095b407dae | |||
| 8d076c897d | |||
| 3e77974fa0 | |||
| 00dd0c48ba | |||
| f550b41724 | |||
| b523237395 | |||
| 0fc4d4c256 | |||
| e4ed5240e7 | |||
| 32037b0135 | |||
| b2674e6fa4 | |||
| cd3b9efe50 | |||
| c7092955b1 | |||
| 1c2441bc76 | |||
| 9035da48a2 | |||
| 83d732d60e | |||
| 1963f268aa | |||
| 1c66f0ba01 | |||
| ac37bebf22 | |||
| 699a8759c5 | |||
| 93dc9fedc8 | |||
| 49dffe83cc | |||
| 2c9f55cbea | |||
| b6dbe1c259 | |||
| e19e338aa9 | |||
| 3f69aec64a | |||
| cea684c065 | |||
| 611e924dc2 | |||
| e439d6adc1 | |||
| 255f9f03e9 | |||
| 7a2f092c6d | |||
| 580949038c | |||
| ba0365d04e | |||
| b0e12b829f | |||
| 13b070d03d | |||
| 729f0df6d5 | |||
| c6d76fcd91 | |||
| 11799d4068 |
@@ -1,6 +1,8 @@
|
||||
name: Backport
|
||||
on:
|
||||
pull_request_target:
|
||||
# Privilege escalation necessary to enable backporting PRs from forks
|
||||
# 🚨 We must not execute any checked out code here.
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||
types:
|
||||
- closed
|
||||
- labeled
|
||||
|
||||
@@ -44,6 +44,8 @@ jobs:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
@@ -67,7 +69,7 @@ jobs:
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) pnpm build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: webapp-${{ matrix.image }}
|
||||
path: apps/web/webapp
|
||||
|
||||
@@ -15,6 +15,8 @@ jobs:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download package
|
||||
working-directory: apps/web
|
||||
@@ -67,7 +69,7 @@ jobs:
|
||||
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
|
||||
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
|
||||
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: element-web.deb
|
||||
path: apps/web/element-web.deb
|
||||
@@ -75,7 +77,7 @@ jobs:
|
||||
|
||||
- name: Publish to packages.element.io
|
||||
if: github.event.release.prerelease == false
|
||||
uses: element-hq/packages.element.io@master
|
||||
uses: element-hq/packages.element.io@master # zizmor: ignore[unpinned-uses]
|
||||
with:
|
||||
file: apps/web/element-web.deb
|
||||
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
|
||||
@@ -29,6 +29,8 @@ jobs:
|
||||
R2_PUBLIC_URL: "https://element-web-develop.element.io"
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
@@ -58,7 +60,7 @@ jobs:
|
||||
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
|
||||
working-directory: apps/web
|
||||
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: webapp
|
||||
path: apps/web/dist/develop.tar.gz
|
||||
@@ -134,4 +136,6 @@ jobs:
|
||||
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- run: |
|
||||
echo "Deployed to ${{ steps.cfp.outputs.url }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Deployed to ${STEPS_CFP_OUTPUTS_URL}" >> $GITHUB_STEP_SUMMARY
|
||||
env:
|
||||
STEPS_CFP_OUTPUTS_URL: ${{ steps.cfp.outputs.url }}
|
||||
|
||||
@@ -19,13 +19,14 @@ jobs:
|
||||
contents: read
|
||||
actions: read
|
||||
steps:
|
||||
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
# We need to fetch all branches and commits so that Nx affected has a base to compare against.
|
||||
fetch-depth: 0
|
||||
# reduce the size of the checkout with tree filtering,
|
||||
# see https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/
|
||||
filter: tree:0
|
||||
persist-credentials: false
|
||||
|
||||
- name: Prepare nx
|
||||
uses: nrwl/nx-set-shas@3e9ad7370203c1e93d109be57f3b72eb0eb511b1 # v4
|
||||
|
||||
@@ -35,6 +35,8 @@ jobs:
|
||||
SITE: ${{ inputs.site || 'staging.element.io' }}
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Load GPG key
|
||||
run: |
|
||||
|
||||
@@ -23,6 +23,7 @@ jobs:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0 # needed for docker-package to be able to calculate the version
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@398d4b0eeef1380460a10c8013a76f728fb906ac # v3
|
||||
|
||||
@@ -21,17 +21,20 @@ jobs:
|
||||
with:
|
||||
repository: element-hq/element-desktop
|
||||
path: element-desktop
|
||||
persist-credentials: false
|
||||
|
||||
- name: Fetch element-web
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
path: element-web
|
||||
persist-credentials: false
|
||||
|
||||
- name: Fetch matrix-js-sdk
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
repository: matrix-org/matrix-js-sdk
|
||||
path: matrix-js-sdk
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
with:
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# taking the artifact and uploading it to Netlify for easier viewing
|
||||
name: Upload End to End Test report to Netlify
|
||||
on:
|
||||
workflow_run:
|
||||
# Privilege escalation necessary to publish to Netlify
|
||||
# 🚨 We must not execute any checked out code here.
|
||||
workflow_run: # zizmor: ignore[dangerous-triggers]
|
||||
workflows: ["End to End Tests"]
|
||||
types:
|
||||
- completed
|
||||
@@ -25,7 +27,7 @@ jobs:
|
||||
actions: read
|
||||
steps:
|
||||
- name: Download HTML report
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -54,6 +54,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
repository: element-hq/element-web
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
@@ -78,7 +79,7 @@ jobs:
|
||||
run: VERSION=$(scripts/get-version-from-git.sh) pnpm build
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: webapp
|
||||
path: apps/web/webapp
|
||||
@@ -132,7 +133,7 @@ jobs:
|
||||
repository: element-hq/element-web
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
name: webapp
|
||||
path: apps/web/webapp
|
||||
@@ -160,11 +161,13 @@ jobs:
|
||||
|
||||
- name: Install Playwright browsers
|
||||
if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
working-directory: apps/web
|
||||
run: pnpm playwright install --with-deps --no-shell
|
||||
|
||||
- name: Install system dependencies for WebKit
|
||||
# Some WebKit dependencies seem to lay outside the cache and will need to be installed separately
|
||||
if: matrix.project == 'WebKit' && steps.playwright-cache.outputs.cache-hit == 'true'
|
||||
working-directory: apps/web
|
||||
run: pnpm playwright install-deps webkit
|
||||
|
||||
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
|
||||
@@ -172,13 +175,15 @@ jobs:
|
||||
working-directory: apps/web
|
||||
run: |
|
||||
pnpm playwright test \
|
||||
--shard "${{ matrix.runner }}/${{ needs.build.outputs.num-runners }}" \
|
||||
--shard "$SHARD" \
|
||||
--project="${{ matrix.project }}" \
|
||||
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
|
||||
env:
|
||||
SHARD: ${{ format('{0}/{1}', matrix.runner, needs.build.outputs.num-runners) }}
|
||||
|
||||
- name: Upload blob report to GitHub Actions Artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: all-blob-reports-${{ matrix.project }}-${{ matrix.runner }}
|
||||
path: apps/web/blob-report
|
||||
@@ -188,7 +193,7 @@ jobs:
|
||||
name: Downstream Playwright tests [element-modules]
|
||||
needs: build
|
||||
if: inputs.skip != true && github.event_name == 'merge_group'
|
||||
uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main
|
||||
uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses]
|
||||
with:
|
||||
webapp-artifact: webapp
|
||||
|
||||
@@ -220,15 +225,16 @@ jobs:
|
||||
|
||||
- name: Download blob reports from GitHub Actions Artifacts
|
||||
if: inputs.skip != true
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
pattern: all-blob-reports-*
|
||||
path: all-blob-reports
|
||||
path: apps/web/all-blob-reports
|
||||
merge-multiple: true
|
||||
|
||||
- name: Merge into HTML Report
|
||||
if: inputs.skip != true
|
||||
run: pnpm playwright merge-reports --reporter=html,./apps/web/playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports
|
||||
working-directory: apps/web
|
||||
run: pnpm playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,@element-hq/element-web-playwright-common/lib/stale-screenshot-reporter.js ./all-blob-reports
|
||||
env:
|
||||
# Only pass creds to the flaky-reporter on main branch runs
|
||||
GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }}
|
||||
@@ -236,11 +242,12 @@ jobs:
|
||||
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
|
||||
- name: Upload HTML report
|
||||
if: always() && inputs.skip != true
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: html-report
|
||||
path: playwright-report
|
||||
path: apps/web/playwright-report
|
||||
retention-days: 14
|
||||
if-no-files-found: error
|
||||
|
||||
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
|
||||
run: exit 1
|
||||
|
||||
@@ -7,7 +7,7 @@ permissions:
|
||||
pull-requests: write # needed to auto-approve PRs
|
||||
jobs:
|
||||
download:
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@6eda3835118f3bc3fb658a1a3c20b7da9d16ae42
|
||||
with:
|
||||
packageManager: pnpm
|
||||
secrets:
|
||||
|
||||
@@ -9,6 +9,6 @@ on:
|
||||
permissions: {} # No permissions needed
|
||||
jobs:
|
||||
upload:
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@6eda3835118f3bc3fb658a1a3c20b7da9d16ae42
|
||||
secrets:
|
||||
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
# and uploading it to netlify
|
||||
name: Upload Preview Build to Netlify
|
||||
on:
|
||||
workflow_run:
|
||||
# Privilege escalation necessary to publish to Netlify
|
||||
# 🚨 We must not execute any checked out code here.
|
||||
workflow_run: # zizmor: ignore[dangerous-triggers]
|
||||
workflows: ["Build"]
|
||||
types:
|
||||
- completed
|
||||
@@ -28,7 +30,7 @@ jobs:
|
||||
Exercise caution. Use test accounts.
|
||||
|
||||
- name: 📥 Download artifact
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
name: Pull Request
|
||||
on:
|
||||
pull_request_target:
|
||||
# Privilege escalation necessary access members of the review teams
|
||||
# 🚨 We must not execute any checked out code here, and be careful around use of user-controlled inputs.
|
||||
pull_request_target: # zizmor: ignore[dangerous-triggers]
|
||||
types: [opened, edited, labeled, unlabeled, synchronize]
|
||||
merge_group:
|
||||
types: [checks_requested]
|
||||
permissions: {}
|
||||
jobs:
|
||||
action:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop # zizmor: ignore[unpinned-uses]
|
||||
permissions:
|
||||
pull-requests: write
|
||||
secrets:
|
||||
|
||||
@@ -9,4 +9,4 @@ jobs:
|
||||
draft:
|
||||
permissions:
|
||||
contents: write
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop # zizmor: ignore[unpinned-uses]
|
||||
|
||||
@@ -7,7 +7,7 @@ concurrency: ${{ github.repository }}-${{ github.workflow }}
|
||||
permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||
jobs:
|
||||
merge:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop # zizmor: ignore[unpinned-uses]
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
with:
|
||||
|
||||
@@ -14,7 +14,7 @@ concurrency: ${{ github.workflow }}
|
||||
permissions: {}
|
||||
jobs:
|
||||
release:
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop # zizmor: ignore[unpinned-uses]
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
- matrix-org/matrix-js-sdk
|
||||
- element-hq/element-web
|
||||
- element-hq/element-desktop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-checks.yml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/release-checks.yml@develop # zizmor: ignore[unpinned-uses]
|
||||
secrets:
|
||||
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
with:
|
||||
@@ -50,6 +50,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
persist-credentials: true
|
||||
- name: Checkout Element Web
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
if: inputs.element-web
|
||||
@@ -60,6 +61,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
persist-credentials: true
|
||||
- name: Checkout Matrix JS SDK
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
if: inputs.matrix-js-sdk
|
||||
@@ -70,6 +72,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
|
||||
persist-credentials: true
|
||||
|
||||
- name: Prepare Git
|
||||
run: |
|
||||
|
||||
@@ -14,6 +14,8 @@ jobs:
|
||||
steps:
|
||||
- name: 🧮 Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: 🔧 Set up node environment
|
||||
|
||||
@@ -13,6 +13,8 @@ jobs:
|
||||
steps:
|
||||
- name: 🧮 Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: 🔧 Pnpm cache
|
||||
@@ -29,7 +31,7 @@ jobs:
|
||||
working-directory: packages/shared-components
|
||||
run: pnpm build:storybook
|
||||
|
||||
- uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: shared-components-storybook
|
||||
path: packages/shared-components/storybook-static
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
needs: build
|
||||
environment: SharedComponents
|
||||
steps:
|
||||
- uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
- uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
name: shared-components-storybook
|
||||
path: storybook-static
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
run: "sudo apt-get install -y tree"
|
||||
|
||||
- name: Download Diffs
|
||||
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7
|
||||
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
run-id: ${{ github.event.workflow_run.id }}
|
||||
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
- name: Upload received images & diffs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: received-images
|
||||
path: packages/shared-components/__vis__/linux
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
sonarqube:
|
||||
name: 🩻 SonarQube
|
||||
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group'
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop
|
||||
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop # zizmor: ignore[unpinned-uses]
|
||||
permissions:
|
||||
actions: read
|
||||
statuses: write
|
||||
|
||||
@@ -49,6 +49,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
@@ -73,6 +75,19 @@ jobs:
|
||||
run: git diff --exit-code
|
||||
if: matrix.assert-diff
|
||||
|
||||
zizmor:
|
||||
name: Zizmor Github Actions lint
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Run zizmor
|
||||
uses: zizmorcore/zizmor-action@0dce2577a4760a2749d8cfb7a84b7d5585ebcb7d # v0.5.0
|
||||
|
||||
i18n:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -95,7 +110,7 @@ jobs:
|
||||
- name: Shared Components
|
||||
path: "packages/shared-components"
|
||||
name: "i18n Check (${{ matrix.name }})"
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
|
||||
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@abf186831e2acb3e98fca13a0711a3fa1364d2b1
|
||||
permissions:
|
||||
pull-requests: read
|
||||
with:
|
||||
|
||||
@@ -13,7 +13,7 @@ permissions: {} # We use ELEMENT_BOT_TOKEN instead
|
||||
|
||||
jobs:
|
||||
sync-labels:
|
||||
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@develop
|
||||
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@dac99c67f08f8f2a079e885ffb682a2f39cd3960
|
||||
with:
|
||||
LABELS: |
|
||||
element-hq/element-meta
|
||||
|
||||
@@ -43,6 +43,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: pnpm cache
|
||||
@@ -70,25 +71,29 @@ jobs:
|
||||
working-directory: apps/web
|
||||
run: |
|
||||
pnpm test \
|
||||
--coverage=${{ env.ENABLE_COVERAGE }} \
|
||||
--coverage=$ENABLE_COVERAGE \
|
||||
--ci \
|
||||
--max-workers ${{ steps.cpu-cores.outputs.count }} \
|
||||
--shard ${{ matrix.runner }}/${{ strategy.job-total }} \
|
||||
--max-workers $MAX_WORKERS \
|
||||
--shard "$SHARD" \
|
||||
--cacheDirectory /tmp/jest_cache
|
||||
env:
|
||||
JEST_SONAR_UNIQUE_OUTPUT_NAME: true
|
||||
|
||||
# tell jest to use coloured output
|
||||
FORCE_COLOR: true
|
||||
MAX_WORKERS: ${{ steps.cpu-cores.outputs.count }}
|
||||
SHARD: ${{ format('{0}/{1}', matrix.runner, strategy.job-total) }}
|
||||
|
||||
- name: Move coverage files into place
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
working-directory: apps/web
|
||||
run: mv coverage/lcov.info coverage/${{ steps.setupNode.outputs.node-version }}-${{ matrix.runner }}.lcov.info
|
||||
run: mv coverage/lcov.info coverage/$NODE_VERSION-${{ matrix.runner }}.lcov.info
|
||||
env:
|
||||
NODE_VERSION: ${{ steps.setupNode.outputs.node-version }}
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: coverage-${{ matrix.runner }}
|
||||
path: |
|
||||
@@ -125,6 +130,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- name: pnpm cache
|
||||
@@ -164,11 +170,11 @@ jobs:
|
||||
|
||||
- name: Run tests
|
||||
working-directory: "packages/shared-components"
|
||||
run: pnpm test:unit --coverage=${{ env.ENABLE_COVERAGE }}
|
||||
run: pnpm test:unit --coverage=$ENABLE_COVERAGE
|
||||
|
||||
- name: Upload Artifact
|
||||
if: env.ENABLE_COVERAGE == 'true'
|
||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
||||
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
|
||||
with:
|
||||
name: coverage-sharedcomponents
|
||||
path: |
|
||||
|
||||
@@ -10,6 +10,8 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
|
||||
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged --concurrent false
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"*": "prettier --write"
|
||||
"*": "prettier --write --ignore-unknown"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,34 @@
|
||||
Changes in [1.12.13](https://github.com/element-hq/element-web/releases/tag/v1.12.13) (2026-03-24)
|
||||
==================================================================================================
|
||||
## 🦖 Deprecations
|
||||
|
||||
* Remove automatic rageshakes on UTD labs feature ([#32778](https://github.com/element-hq/element-web/pull/32778)). Contributed by @Half-Shot.
|
||||
* Remove automaticErrorReporting labs feature ([#32781](https://github.com/element-hq/element-web/pull/32781)). Contributed by @Half-Shot.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
* Upgrade Element Call for new picture-in-picture designs ([#32816](https://github.com/element-hq/element-web/pull/32816)). Contributed by @robintown.
|
||||
* Room list: add sections to shared components ([#32735](https://github.com/element-hq/element-web/pull/32735)). Contributed by @florianduros.
|
||||
* feat: Devtool for sticky events MSC4354 ([#32741](https://github.com/element-hq/element-web/pull/32741)). Contributed by @BillCarsonFr.
|
||||
* Port URL Preview components to MVVM ([#32525](https://github.com/element-hq/element-web/pull/32525)). Contributed by @Half-Shot.
|
||||
* Add support for Widget \& Room Header Buttons module APIs ([#32734](https://github.com/element-hq/element-web/pull/32734)). Contributed by @dbkr.
|
||||
* Port over linkifyJS to shared-components. ([#32731](https://github.com/element-hq/element-web/pull/32731)). Contributed by @Half-Shot.
|
||||
* Redesign widget pip and move into shared component ([#32654](https://github.com/element-hq/element-web/pull/32654)). Contributed by @toger5.
|
||||
* Implement customisations \& login component Module API 1.11.0 ([#32687](https://github.com/element-hq/element-web/pull/32687)). Contributed by @t3chguy.
|
||||
* Realign MessageActionBar to Figma designs ([#32722](https://github.com/element-hq/element-web/pull/32722)). Contributed by @t3chguy.
|
||||
* Implement new widget permissions module api ([#32565](https://github.com/element-hq/element-web/pull/32565)). Contributed by @langleyd.
|
||||
|
||||
## 🐛 Bug Fixes
|
||||
|
||||
* [Backport staging] Fix soft crash of room list when trying to open a room ([#32872](https://github.com/element-hq/element-web/pull/32872)). Contributed by @RiotRobot.
|
||||
* Fix "key storage out of sync" appearing when key storage is actually fine ([#32811](https://github.com/element-hq/element-web/pull/32811)). Contributed by @andybalaam.
|
||||
* Fix remove button styling in local address list of room settings ([#32798](https://github.com/element-hq/element-web/pull/32798)). Contributed by @florianduros.
|
||||
* Always check \& update the users timezone on their profile on startup ([#32764](https://github.com/element-hq/element-web/pull/32764)). Contributed by @Half-Shot.
|
||||
* Fix nx configuration to actually run type linter ([#32776](https://github.com/element-hq/element-web/pull/32776)). Contributed by @richvdh.
|
||||
* Fix expand space panel button not being shown on keyboard focus ([#32746](https://github.com/element-hq/element-web/pull/32746)). Contributed by @t3chguy.
|
||||
* Reset key storage if restoring from Recovery encounters the wrong decryption key ([#32668](https://github.com/element-hq/element-web/pull/32668)). Contributed by @andybalaam.
|
||||
|
||||
|
||||
Changes in [1.12.12](https://github.com/element-hq/element-web/releases/tag/v1.12.12) (2026-03-10)
|
||||
==================================================================================================
|
||||
## ✨ Features
|
||||
|
||||
@@ -46,6 +46,9 @@ As for your PR description, it should include these things:
|
||||
- Add comments to the diff for the reviewer that might help them to understand
|
||||
why the change is necessary or how they might better understand and review it.
|
||||
|
||||
Please **_do not use force push_** in your PRs. Doing so means we can't see what
|
||||
has changed. We use squash merge to get a "clean" git history.
|
||||
|
||||
### Changelogs
|
||||
|
||||
There's no need to manually add Changelog entries: we use information in the
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"*": "prettier --write",
|
||||
"*": "prettier --write --ignore-unknown",
|
||||
"src/**/*.(ts|tsx)": ["eslint --fix"],
|
||||
"scripts/**/*.(ts|tsx)": ["eslint --fix"],
|
||||
"module_system/**/*.(ts|tsx)": ["eslint --fix"],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# syntax=docker.io/docker/dockerfile:1.21-labs@sha256:2e681d22e86e738a057075f930b81b2ab8bc2a34cd16001484a7453cfa7a03fb
|
||||
|
||||
# Builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:38edad6b2e5962120f5144ff9dd3dbd223c7f140ba6fa03920d62d28b021402b AS builder
|
||||
FROM --platform=$BUILDPLATFORM node:24-bullseye@sha256:d83f76ec9956d35fe14221022e8b5a87a5de6fcdb4edb1187eddeb7dc80cba71 AS builder
|
||||
|
||||
# Support custom branch of the js-sdk. This also helps us build images of element-web develop.
|
||||
ARG USE_CUSTOM_SDKS=false
|
||||
@@ -20,7 +20,7 @@ RUN /src/scripts/docker-package.sh
|
||||
RUN cp /src/apps/web/config.sample.json /src/apps/web/webapp/config.json
|
||||
|
||||
# App
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:c9448f9aaf2dee3dccfe0d2e51d6927cc9fbfdbcada66b0b01c0759816d86a5b
|
||||
FROM nginxinc/nginx-unprivileged:alpine-slim@sha256:800307aaf07c143f5d90aa9d4269cc1971dcfe5aee7cabd11579ac4c6bcf198f
|
||||
|
||||
# Need root user to install packages & manipulate the usr directory
|
||||
USER root
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": false,
|
||||
"room_directory": {
|
||||
"servers": ["matrix.org", "gitter.im"]
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"https://scalar-staging.vector.im/api"
|
||||
],
|
||||
"bug_report_endpoint_url": "https://rageshakes.element.io/api/submit",
|
||||
"uisi_autorageshake_app": "element-auto-uisi",
|
||||
"show_labs_settings": true,
|
||||
"room_directory": {
|
||||
"servers": ["matrix.org", "gitter.im"]
|
||||
|
||||
@@ -17,6 +17,9 @@ const config: Config = {
|
||||
// This is needed to be able to load dual CJS/ESM WASM packages e.g. rust crypto & matrix-wywiwyg
|
||||
customExportConditions: ["browser", "node"],
|
||||
},
|
||||
transform: {
|
||||
"\\.[jt]sx?$": "babel-jest",
|
||||
},
|
||||
testMatch: ["<rootDir>/test/**/*-test.[tj]s?(x)"],
|
||||
globalSetup: "<rootDir>/test/globalSetup.ts",
|
||||
setupFiles: ["jest-canvas-mock", "web-streams-polyfill/polyfill"],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "element-web",
|
||||
"version": "1.12.12",
|
||||
"version": "1.12.13",
|
||||
"description": "Element: the future of secure communication",
|
||||
"author": "New Vector Ltd.",
|
||||
"repository": {
|
||||
@@ -78,14 +78,10 @@
|
||||
"jsrsasign": "^11.0.0",
|
||||
"jszip": "^3.7.0",
|
||||
"katex": "^0.16.0",
|
||||
"linkify-html": "4.3.2",
|
||||
"linkify-react": "4.3.2",
|
||||
"linkify-string": "4.3.2",
|
||||
"linkifyjs": "4.3.2",
|
||||
"lodash": "npm:lodash-es@^4.17.21",
|
||||
"maplibre-gl": "^5.0.0",
|
||||
"matrix-encrypt-attachment": "^1.0.3",
|
||||
"matrix-js-sdk": "41.1.0",
|
||||
"matrix-js-sdk": "41.2.0",
|
||||
"matrix-widget-api": "^1.16.1",
|
||||
"memoize-one": "^6.0.0",
|
||||
"mime": "^4.0.4",
|
||||
@@ -93,7 +89,7 @@
|
||||
"opus-recorder": "^8.0.3",
|
||||
"pako": "^2.0.3",
|
||||
"png-chunks-extract": "^1.0.0",
|
||||
"posthog-js": "1.347.2",
|
||||
"posthog-js": "1.356.1",
|
||||
"qrcode": "1.5.4",
|
||||
"re-resizable": "6.11.2",
|
||||
"react": "catalog:",
|
||||
@@ -105,7 +101,7 @@
|
||||
"react-transition-group": "^4.4.1",
|
||||
"rfc4648": "^1.4.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"sanitize-html": "2.17.0",
|
||||
"sanitize-html": "2.17.1",
|
||||
"tar-js": "^0.3.0",
|
||||
"ua-parser-js": "1.0.40",
|
||||
"uuid": "^13.0.0",
|
||||
@@ -129,9 +125,8 @@
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/preset-react": "^7.12.10",
|
||||
"@babel/preset-typescript": "^7.12.7",
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@casualbot/jest-sonar-reporter": "2.5.0",
|
||||
"@element-hq/element-call-embedded": "0.16.3",
|
||||
"@element-hq/element-call-embedded": "0.18.0",
|
||||
"@element-hq/element-web-playwright-common": "catalog:",
|
||||
"@element-hq/element-web-playwright-common-local": "workspace:*",
|
||||
"@fetch-mock/jest": "^0.2.20",
|
||||
@@ -139,7 +134,7 @@
|
||||
"@peculiar/webcrypto": "^1.4.3",
|
||||
"@playwright/test": "catalog:",
|
||||
"@principalstudio/html-webpack-inject-preload": "^1.2.7",
|
||||
"@sentry/webpack-plugin": "^4.0.0",
|
||||
"@sentry/webpack-plugin": "^5.0.0",
|
||||
"@stylistic/eslint-plugin": "^5.0.0",
|
||||
"@svgr/webpack": "^8.0.0",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
@@ -179,14 +174,14 @@
|
||||
"babel-loader": "^10.0.0",
|
||||
"babel-plugin-jsx-remove-data-test-id": "^3.0.0",
|
||||
"blob-polyfill": "^9.0.0",
|
||||
"copy-webpack-plugin": "^13.0.0",
|
||||
"copy-webpack-plugin": "^14.0.0",
|
||||
"css-loader": "^7.0.0",
|
||||
"css-minimizer-webpack-plugin": "^7.0.0",
|
||||
"css-minimizer-webpack-plugin": "^8.0.0",
|
||||
"dotenv": "^17.0.0",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-config-prettier": "^10.0.0",
|
||||
"eslint-plugin-deprecate": "0.8.7",
|
||||
"eslint-plugin-deprecate": "0.9.0",
|
||||
"eslint-plugin-import": "^2.25.4",
|
||||
"eslint-plugin-jest": "^29.0.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
@@ -210,6 +205,7 @@
|
||||
"matrix-web-i18n": "catalog:",
|
||||
"mini-css-extract-plugin": "2.10.0",
|
||||
"modernizr": "^3.12.0",
|
||||
"playwright-core": "catalog:",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-easings": "4.0.0",
|
||||
"postcss-hexrgba": "2.1.0",
|
||||
@@ -250,6 +246,6 @@
|
||||
"engines": {
|
||||
"node": ">=22.18"
|
||||
},
|
||||
"packageManager": "pnpm@10.29.3+sha512.498e1fb4cca5aa06c1dcf2611e6fafc50972ffe7189998c409e90de74566444298ffe43e6cd2acdc775ba1aa7cc5e092a8b7054c811ba8c5770f84693d33d2dc",
|
||||
"packageManager": "pnpm@10.30.3+sha512.c961d1e0a2d8e354ecaa5166b822516668b7f44cb5bd95122d590dd81922f606f5473b6d23ec4a5be05e7fcd18e8488d47d978bbe981872f1145d06e9a740017",
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -245,9 +245,9 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
await expect(tile.getByRole("region", { name: "Audio player" })).toBeVisible();
|
||||
|
||||
// Assert that replied audio file is rendered as file button inside ReplyChain
|
||||
const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody_info[role='button']");
|
||||
const button = tile.locator(".mx_ReplyChain_wrapper .mx_MFileBody [role='button']");
|
||||
// Assert that the file button has file name
|
||||
await expect(button.locator(".mx_MFileBody_info_filename")).toBeVisible();
|
||||
await expect(button.locator("span")).toBeVisible();
|
||||
|
||||
await takeSnapshots(page, app, "Selected EventTile of audio player with a reply");
|
||||
},
|
||||
@@ -307,9 +307,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
|
||||
// Assert that the file button contains the name of the file sent at first
|
||||
await expect(
|
||||
replyChain
|
||||
.locator(".mx_MFileBody_info[role='button']")
|
||||
.locator(".mx_MFileBody_info_filename", { hasText: "upload-first.ogg" }),
|
||||
replyChain.locator(".mx_MFileBody [role='button']").locator("span", { hasText: "upload-first.ogg" }),
|
||||
).toBeVisible();
|
||||
|
||||
// Take snapshots
|
||||
@@ -357,9 +355,7 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
|
||||
const composer = thread.locator(".mx_MessageComposer--compact");
|
||||
// Assert that the reply preview contains audio ReplyTile the file info button
|
||||
await expect(
|
||||
composer.locator(".mx_ReplyPreview .mx_ReplyTile .mx_MFileBody_info[role='button']"),
|
||||
).toBeVisible();
|
||||
await expect(composer.locator(".mx_ReplyPreview .mx_ReplyTile .mx_MFileBody [role='button']")).toBeVisible();
|
||||
|
||||
// Select :smile: emoji and send it
|
||||
await composer.getByTestId("basicmessagecomposer").fill(":smile:");
|
||||
@@ -367,6 +363,6 @@ test.describe("Audio player", { tag: ["@no-firefox", "@no-webkit"] }, () => {
|
||||
await composer.getByTestId("basicmessagecomposer").press("Enter");
|
||||
|
||||
// Assert that the file name is rendered on the file button
|
||||
await expect(threadTile.locator(".mx_ReplyTile .mx_MFileBody_info[role='button']")).toBeVisible();
|
||||
await expect(threadTile.locator(".mx_ReplyTile .mx_MFileBody [role='button']")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -337,7 +337,7 @@ export async function disableKeyBackup(app: ElementAppPage): Promise<void> {
|
||||
if (await keyStorageToggle.isChecked()) {
|
||||
await encryptionTab.getByRole("switch", { name: "Allow key storage" }).click();
|
||||
await encryptionTab.getByRole("button", { name: "Delete key storage" }).click();
|
||||
await encryptionTab.getByRole("switch", { name: "Allow key storage" }).isVisible();
|
||||
await expect(encryptionTab.getByRole("switch", { name: "Allow key storage" })).toBeVisible();
|
||||
|
||||
// Wait for the update to account data to stick
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000));
|
||||
|
||||
@@ -35,6 +35,6 @@ test.describe("Devtools", () => {
|
||||
await input.fill("https://example.com");
|
||||
await input.press("Enter");
|
||||
// expect EW NOT to reload
|
||||
await page.getByText("Saved").isVisible();
|
||||
await expect(page.getByText("Saved")).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -48,7 +48,7 @@ test.describe("Create Knock Room", () => {
|
||||
await app.settings.openRoomSettings("Security & Privacy");
|
||||
|
||||
const settingsGroup = page.getByRole("group", { name: "Access" });
|
||||
await expect(settingsGroup.getByRole("radio", { name: "Private (invite only)" })).toBeChecked();
|
||||
await expect(settingsGroup.getByRole("radio", { name: "Invite only" })).toBeChecked();
|
||||
await settingsGroup.getByText("Ask to join").click();
|
||||
|
||||
// Room should have a knock join rule
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2026 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
test.describe("Message links", () => {
|
||||
test.use({
|
||||
displayName: "Alice",
|
||||
room: async ({ user, app, bot }, use) => {
|
||||
const roomId = await app.client.createRoom({ name: "Test room" });
|
||||
await use({ roomId });
|
||||
},
|
||||
});
|
||||
for (const link of ["https://example.org", "example.org", "ftp://example.org"]) {
|
||||
test(`should linkify a regular link '${link}'`, async ({ page, user, app, room }) => {
|
||||
await page.goto(`#/room/${room.roomId}`);
|
||||
// Needs to be unformatted so we test linkifing
|
||||
await app.client.sendMessage(room.roomId, `Check out ${link}`);
|
||||
const linkElement = page.locator(".mx_EventTile_last").getByRole("link", { name: link });
|
||||
await app.timeline.scrollToBottom();
|
||||
await expect(linkElement).toBeVisible();
|
||||
});
|
||||
}
|
||||
test("should linkify a User ID", async ({ page, user, app, room }) => {
|
||||
await page.goto(`#/room/${room.roomId}`);
|
||||
// Needs to be unformatted so we test linkifing
|
||||
await app.client.sendMessage(room.roomId, `Check out @bob:example.org`);
|
||||
const linkElement = page.locator(".mx_EventTile_last").getByRole("link", { name: "@bob:example.org" });
|
||||
await expect(linkElement).toHaveAttribute("href", `https://matrix.to/#/@bob:example.org`);
|
||||
});
|
||||
test("should linkify a Room alias", async ({ page, user, app, room }) => {
|
||||
await page.goto(`#/room/${room.roomId}`);
|
||||
// Needs to be unformatted so we test linkifing
|
||||
await app.client.sendMessage(room.roomId, "Check out #aroom:example.org");
|
||||
const linkElement = page.locator(".mx_EventTile_last").getByRole("link", { name: "#aroom:example.org" });
|
||||
await expect(linkElement).toHaveAttribute("href", "https://matrix.to/#/#aroom:example.org");
|
||||
});
|
||||
test("should linkify text inside a URL preview", async ({ page, user, app, room }) => {
|
||||
await page.route(/.*\/_matrix\/(client\/v1\/media|media\/v3)\/preview_url.*/, (route, request) => {
|
||||
const requestedPage = new URL(request.url()).searchParams.get("url");
|
||||
expect(requestedPage).toEqual("https://example.org/");
|
||||
return route.fulfill({
|
||||
json: {
|
||||
"og:title": "A simple site",
|
||||
"og:description": "And with a brief description containing https://example.org/another-link",
|
||||
},
|
||||
});
|
||||
});
|
||||
await page.goto(`#/room/${room.roomId}`);
|
||||
await app.client.sendMessage(room.roomId, "Check out https://example.org/");
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last").getByRole("link", { name: "https://example.org/another-link" }),
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2026 New Vector Ltd.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
import { test, expect } from "../../element-web-test";
|
||||
|
||||
test.describe("Topic links", () => {
|
||||
test.use({
|
||||
displayName: "Alice",
|
||||
room: async ({ user, app, bot }, use) => {
|
||||
const roomId = await app.client.createRoom({ name: "Test room" });
|
||||
await use({ roomId });
|
||||
},
|
||||
});
|
||||
for (const link of [
|
||||
"https://example.org",
|
||||
"example.org",
|
||||
"ftp://example.org",
|
||||
"#aroom:example.org",
|
||||
"@alice:example.org",
|
||||
]) {
|
||||
// Playwright treats '@' as a tag, so replace it to be safe
|
||||
test(`should linkify plaintext '${link.replace("@", "_@")}'`, async ({ page, user, app, room }) => {
|
||||
await app.client.sendStateEvent(
|
||||
room.roomId,
|
||||
"m.room.topic",
|
||||
{
|
||||
"m.topic": {
|
||||
// Deliberately no HTML version.
|
||||
"m.text": [
|
||||
{
|
||||
body: `An interesting room topic containing ${link}`,
|
||||
},
|
||||
],
|
||||
},
|
||||
"topic": `An interesting room topic containing ${link}`,
|
||||
},
|
||||
"",
|
||||
);
|
||||
await page.goto(`#/room/${room.roomId}`);
|
||||
await expect(page.getByTestId("topic").getByRole("link", { name: link })).toBeVisible();
|
||||
const locator = await app.toggleRoomInfoPanel();
|
||||
await expect(locator.getByRole("link", { name: link })).toBeVisible();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -85,7 +85,7 @@ test.describe("FilePanel", () => {
|
||||
await expect(filePanelMessageList.locator(".mx_EventTile")).toHaveCount(3);
|
||||
|
||||
// Assert that the download links are rendered
|
||||
await expect(filePanelMessageList.locator(".mx_MFileBody_download,.mx_MFileBody_info")).toHaveCount(3);
|
||||
await expect(filePanelMessageList.locator(".mx_MFileBody")).toHaveCount(3);
|
||||
|
||||
// Assert that the sender of the files is rendered on all of the tiles
|
||||
await expect(filePanelMessageList.getByText(NAME)).toHaveCount(3);
|
||||
@@ -103,9 +103,7 @@ test.describe("FilePanel", () => {
|
||||
|
||||
// Detect the JSON file
|
||||
// Assert that the tile is rendered as a button
|
||||
const file = filePanelMessageList.locator(
|
||||
".mx_EventTile_mediaLine .mx_MFileBody .mx_MFileBody_info[role='button'] .mx_MFileBody_info_filename",
|
||||
);
|
||||
const file = filePanelMessageList.locator(".mx_EventTile_mediaLine .mx_MFileBody [role='button']");
|
||||
// Assert that the file name is rendered inside the button with ellipsis
|
||||
await expect(file.getByText(/matrix.*?\.json/)).toBeVisible();
|
||||
|
||||
@@ -177,8 +175,7 @@ test.describe("FilePanel", () => {
|
||||
const tile = page.locator(".mx_FilePanel .mx_EventTile");
|
||||
// Assert that the file size is displayed in kibibytes, not kilobytes (1000 bytes)
|
||||
// See: https://github.com/vector-im/element-web/issues/24866
|
||||
await expect(tile.locator(".mx_MFileBody_info_filename", { hasText: size })).toBeVisible();
|
||||
await expect(tile.locator(".mx_MFileBody_info", { hasText: size })).toBeVisible();
|
||||
await expect(tile.locator(".mx_MFileBody [data-type='info']", { hasText: size })).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -194,7 +191,7 @@ test.describe("FilePanel", () => {
|
||||
".mx_FilePanel .mx_RoomView_MessageList .mx_EventTile_mediaLine.mx_EventTile_image .mx_MImageBody",
|
||||
);
|
||||
|
||||
const link = imageBody.locator(".mx_MFileBody_download a");
|
||||
const link = imageBody.locator(".mx_MFileBody a");
|
||||
|
||||
const downloadPromise = page.waitForEvent("download");
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||
const settingsGroupAccess = page.getByRole("group", { name: "Access" });
|
||||
const settingsGroupHistory = page.getByRole("group", { name: "Who can read history?" });
|
||||
|
||||
await settingsGroupAccess.getByText("Public").click();
|
||||
await settingsGroupAccess.getByText("Anyone", { exact: true }).click();
|
||||
await settingsGroupHistory.getByText("Anyone").click();
|
||||
|
||||
// Test that we have the warning appear.
|
||||
@@ -72,7 +72,7 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||
await expect(axe).toHaveNoViolations();
|
||||
await expect(settings).toMatchScreenshot("room-security-settings-world-readable.png");
|
||||
|
||||
await settingsGroupAccess.getByText("Private (invite only)").click();
|
||||
await settingsGroupAccess.getByText("Invite only").click();
|
||||
// Element should have automatically set the room to "sharing" history visibility
|
||||
await expect(settingsGroupHistory.getByText("Members (full history)")).toBeChecked();
|
||||
},
|
||||
@@ -87,7 +87,7 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||
const settingsGroupAccess = page.getByRole("group", { name: "Access" });
|
||||
const settingsGroupHistory = page.getByRole("group", { name: "Who can read history?" });
|
||||
|
||||
await settingsGroupAccess.getByText("Public").click();
|
||||
await settingsGroupAccess.getByText("Anyone", { exact: true }).click();
|
||||
await settingsGroupHistory.getByText("Anyone").click();
|
||||
|
||||
// De-op ourselves
|
||||
@@ -108,7 +108,7 @@ test.describe("Roles & Permissions room settings tab", () => {
|
||||
|
||||
await app.settings.switchTab("Security & Privacy");
|
||||
|
||||
await settingsGroupAccess.getByText("Private (invite only)").click();
|
||||
await settingsGroupAccess.getByText("Invite only").click();
|
||||
// Element should have automatically set the room to "sharing" history visibility
|
||||
const errorDialog = page.getByRole("heading", { name: "Cannot make room private" });
|
||||
await expect(errorDialog).toBeVisible();
|
||||
|
||||
@@ -384,7 +384,7 @@ test.describe("Spaces", () => {
|
||||
});
|
||||
await app.viewSpaceByName("My space");
|
||||
await page.getByLabel("Settings", { exact: true }).click();
|
||||
await app.settings.switchTab("Visibility");
|
||||
await app.settings.switchTab("Security & Privacy");
|
||||
|
||||
axe.disableRules("color-contrast"); // XXX: Inheriting colour contrast issues from room view.
|
||||
await expect(axe).toHaveNoViolations();
|
||||
|
||||
@@ -203,8 +203,6 @@ test.describe("Spotlight", () => {
|
||||
.locator("..")
|
||||
.locator("[role=menuitemradio]")
|
||||
.click();
|
||||
await page.waitForTimeout(3_600_000);
|
||||
|
||||
await page.waitForTimeout(500); // wait for the dialog to settle
|
||||
|
||||
const resultLocator = spotlight.results;
|
||||
|
||||
@@ -441,7 +441,7 @@ test.describe("Threads", () => {
|
||||
textbox = locator.getByRole("textbox", { name: "Send an unencrypted message…" });
|
||||
await textbox.fill("Hello Mr. User");
|
||||
await textbox.press("Enter");
|
||||
await expect(locator.locator(".mx_EventTile_last").getByText("Hello Mr. User")).toBeAttached();
|
||||
await expect(locator.locator(".mx_EventTile_last").getByText("Hello Mr. User")).toBeVisible();
|
||||
// Close thread
|
||||
await locator.getByTestId("base-card-close-button").click();
|
||||
|
||||
@@ -454,8 +454,8 @@ test.describe("Threads", () => {
|
||||
await expect(page.locator(".mx_ThreadView_timelinePanelWrapper")).toHaveCount(1);
|
||||
|
||||
locator = page.locator(".mx_BaseCard");
|
||||
await expect(locator.locator(".mx_EventTile").first().getByText("Hello Mr. Bot")).toBeAttached();
|
||||
await expect(locator.locator(".mx_EventTile").last().getByText("Hello Mr. User")).toBeAttached();
|
||||
await expect(locator.locator(".mx_EventTile").first().getByText("Hello Mr. Bot")).toBeVisible();
|
||||
await expect(locator.locator(".mx_EventTile").last().getByText("Hello Mr. User")).toBeVisible();
|
||||
});
|
||||
|
||||
test("navigate through right panel", { tag: "@screenshot" }, async ({ page, app, user }) => {
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type Page } from "playwright-core";
|
||||
import { type StartedHomeserverContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers";
|
||||
|
||||
import { test, expect } from "../../element-web-test";
|
||||
import { Bot } from "../../pages/bot";
|
||||
import { type ElementAppPage } from "../../pages/ElementAppPage";
|
||||
import { type Credentials } from "../../plugins/homeserver";
|
||||
|
||||
test.describe("Event List Summary", () => {
|
||||
test.use({
|
||||
displayName: "Finch",
|
||||
});
|
||||
|
||||
test(
|
||||
"should display a single join message on its own",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// When the bot joins the room
|
||||
await bot.joinRoom(roomId);
|
||||
|
||||
// Then we say that in a generic event list summary
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("bot_joined_the_room.png", ignoreTimestamps);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should display a single ban message on its own",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// Given the bot is in the room
|
||||
await bot.joinRoom(roomId);
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// And we said something to separate out the messages
|
||||
await app.client.sendMessage(roomId, "Saying something");
|
||||
|
||||
// When we ban the bot
|
||||
await app.client.ban(roomId, bot.credentials.userId);
|
||||
|
||||
// Then we say that
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "banned",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot("bot_was_banned.png", ignoreTimestamps);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should display multiple join/leave messages as a group",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// Given the bot is in the room
|
||||
await bot.joinRoom(roomId);
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// When we perform multiple actions on it
|
||||
await app.client.kick(roomId, bot.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
|
||||
// Then those actions are gathered into a single summary
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "and joined",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_join_leave_messages.png",
|
||||
ignoreTimestamps,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should display multiple messages as a group",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// Given the bot is in the room
|
||||
await bot.joinRoom(roomId);
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// When we perform multiple actions on it, including a ban
|
||||
await app.client.ban(roomId, bot.credentials.userId);
|
||||
await app.client.unban(roomId, bot.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
|
||||
// Then those actions are gathered into a single summary
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "and joined",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_join_ban_messages.png",
|
||||
ignoreTimestamps,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should display join/leave messages for multiple people as a group",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// Given the bot is in the room
|
||||
const bot2 = new Bot(page, homeserver, {
|
||||
displayName: "MyBot2",
|
||||
autoAcceptInvites: false,
|
||||
});
|
||||
await bot2.prepareClient();
|
||||
await app.client.inviteUser(roomId, bot2.credentials.userId);
|
||||
await app.client.sendMessage(roomId, "I invited MyBot2...");
|
||||
await bot.joinRoom(roomId);
|
||||
await bot2.joinRoom(roomId);
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot2 joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// When we perform multiple actions on both bots
|
||||
await app.client.kick(roomId, bot.credentials.userId);
|
||||
await app.client.kick(roomId, bot2.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot2.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
await bot2.joinRoom(roomId);
|
||||
|
||||
// Then those actions are gathered into a single summary
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "joined, were removed, were invited, and joined",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(page.locator('div[aria-label="3 members"]')).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot, bot2);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_people_join_leave_messages.png",
|
||||
ignoreTimestampsRightColumnAndHeader,
|
||||
);
|
||||
|
||||
// And when we expand the summary
|
||||
// Note: we can't include "expand" in the screenshot because it
|
||||
// moves around, but at least we know it exists because we click it
|
||||
// here.
|
||||
await page.getByRole("button", { name: "expand" }).nth(3).click();
|
||||
|
||||
// Then we see all the individual actions
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "removed MyBot2",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot, bot2);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_people_join_leave_messages_expanded.png",
|
||||
ignoreTimestampsRightColumnAndHeader,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
"should display join/ban messages for multiple people as a group",
|
||||
{ tag: "@screenshot" },
|
||||
async ({ app, homeserver, page, user }) => {
|
||||
const { bot, roomId } = await setupRoom(app, homeserver, page, user);
|
||||
|
||||
// Given the bot is in the room
|
||||
const bot2 = new Bot(page, homeserver, {
|
||||
displayName: "MyBot2 with very long display name causing wrapping",
|
||||
autoAcceptInvites: false,
|
||||
});
|
||||
await bot2.prepareClient();
|
||||
await app.client.inviteUser(roomId, bot2.credentials.userId);
|
||||
await app.client.sendMessage(roomId, "I invited MyBot2...");
|
||||
await bot.joinRoom(roomId);
|
||||
await bot2.joinRoom(roomId);
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "MyBot2 with very long display name causing wrapping joined the room",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
// When we ban bot1 but not bot2
|
||||
await app.client.ban(roomId, bot.credentials.userId);
|
||||
await app.client.unban(roomId, bot.credentials.userId);
|
||||
await app.client.kick(roomId, bot2.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await app.client.inviteUser(roomId, bot2.credentials.userId);
|
||||
await bot.joinRoom(roomId);
|
||||
await bot2.joinRoom(roomId);
|
||||
|
||||
// Then those actions are gathered into a single summary
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "was removed, was invited, and joined",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await expect(page.locator('div[aria-label="3 members"]')).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot, bot2);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_people_ban_messages.png",
|
||||
ignoreTimestampsRightColumnAndHeader,
|
||||
);
|
||||
|
||||
// And when we expand the summary
|
||||
// Note: we can't include "expand" in the screenshot because it
|
||||
// moves around, but at least we know it exists because we click it
|
||||
// here.
|
||||
await page.getByRole("button", { name: "expand" }).nth(3).click();
|
||||
|
||||
// Then we see all the individual actions
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: "removed MyBot2",
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
await replaceBotIds(page, bot, bot2);
|
||||
await expect(page.locator(".mx_MainSplit")).toMatchScreenshot(
|
||||
"multiple_people_ban_messages_expanded.png",
|
||||
ignoreTimestampsRightColumnAndHeader,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
const ignoreTimestamps = {
|
||||
css: ".mx_MessageTimestamp,.mx_TopUnreadMessagesBar { visibility: hidden; },",
|
||||
};
|
||||
|
||||
const ignoreTimestampsRightColumnAndHeader = {
|
||||
css:
|
||||
".mx_MessageTimestamp," +
|
||||
".mx_GenericEventListSummary_toggle," +
|
||||
".mx_ReadReceiptGroup," +
|
||||
".mx_RoomHeader, " +
|
||||
".mx_TopUnreadMessagesBar " +
|
||||
"{ visibility: hidden; }",
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a room, join it, create a bot and invite it to the room.
|
||||
*/
|
||||
async function setupRoom(app: ElementAppPage, homeserver: StartedHomeserverContainer, page: Page, user: Credentials) {
|
||||
const roomId = await app.client.createRoom({ name: "My room" });
|
||||
await page.goto(`/#/room/${roomId}`);
|
||||
|
||||
await expect(
|
||||
page.locator(".mx_RoomView_body .mx_GenericEventListSummary[data-layout='group']", {
|
||||
hasText: `${user.displayName} created and configured the room.`,
|
||||
}),
|
||||
).toBeVisible();
|
||||
|
||||
const bot = new Bot(page, homeserver, {
|
||||
displayName: "MyBot",
|
||||
autoAcceptInvites: false,
|
||||
});
|
||||
await bot.prepareClient();
|
||||
await app.client.inviteUser(roomId, bot.credentials.userId);
|
||||
await app.client.sendMessage(roomId, "I invited MyBot...");
|
||||
|
||||
return { bot, roomId };
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the ID of the supplied bot in the page and replace it with a known string.
|
||||
*
|
||||
* This allows us to create consistent screenshots.
|
||||
*/
|
||||
async function replaceBotIds(page: Page, bot: Bot, bot2?: Bot) {
|
||||
await page.evaluate(
|
||||
([bot1UserId, bot2UserId]) => {
|
||||
for (const el of document.querySelectorAll("div.mx_TextualEvent")) {
|
||||
if ("innerText" in el) {
|
||||
el.innerText = (el.innerText as any as string).replaceAll(bot1UserId, "<<replaced_bot1_id>>");
|
||||
el.innerText = (el.innerText as any as string).replaceAll(bot2UserId, "<<replaced_bot2_id>>");
|
||||
}
|
||||
}
|
||||
},
|
||||
[bot.credentials.userId, bot2?.credentials?.userId ?? "no_bot_2_to_replace"],
|
||||
);
|
||||
}
|
||||
@@ -779,7 +779,7 @@ test.describe("Timeline", () => {
|
||||
// Assert that the file size is displayed in kibibytes (1024 bytes), not kilobytes (1000 bytes)
|
||||
// See: https://github.com/vector-im/element-web/issues/24866
|
||||
await expect(
|
||||
page.locator(".mx_EventTile_last .mx_MFileBody_info_filename").getByText(/1.12 KB/),
|
||||
page.locator(".mx_EventTile_last .mx_MFileBody [data-type='info']").getByText(/1.12 KB/),
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
|
||||
@@ -508,15 +508,16 @@ test.describe("Element Call", () => {
|
||||
|
||||
await openAndJoinCall(page);
|
||||
await app.viewRoomByName("OtherRoom");
|
||||
const pipContainer = page.locator(".mx_WidgetPip");
|
||||
const pipContainer = page.getByTestId("widget-pip-container");
|
||||
|
||||
// We should have a PiP container here.
|
||||
await expect(pipContainer).toBeVisible();
|
||||
|
||||
// Leave the call.
|
||||
const overlay = page.locator(".mx_WidgetPip_overlay");
|
||||
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
||||
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
||||
const fakeWidget = page.locator('iframe[title="Element Call"]').contentFrame();
|
||||
|
||||
// await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
||||
await fakeWidget.getByRole("button", { name: "Close", exact: true }).click();
|
||||
|
||||
// PiP container goes.
|
||||
await expect(pipContainer).not.toBeVisible();
|
||||
@@ -541,15 +542,14 @@ test.describe("Element Call", () => {
|
||||
|
||||
await openAndJoinCall(page);
|
||||
await app.viewRoomByName("OtherRoom");
|
||||
const pipContainer = page.locator(".mx_WidgetPip");
|
||||
const pipContainer = page.getByTestId("widget-pip-container");
|
||||
|
||||
// We should have a PiP container here.
|
||||
await expect(pipContainer).toBeVisible();
|
||||
|
||||
// Leave the call.
|
||||
const overlay = page.locator(".mx_WidgetPip_overlay");
|
||||
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
||||
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
||||
const fakeWidget = page.locator('iframe[title="Element Call"]').contentFrame();
|
||||
await fakeWidget.getByRole("button", { name: "Close", exact: true }).click();
|
||||
|
||||
// PiP container goes.
|
||||
await expect(pipContainer).not.toBeVisible();
|
||||
@@ -578,15 +578,16 @@ test.describe("Element Call", () => {
|
||||
await openAndJoinCall(page, true);
|
||||
|
||||
await app.viewRoomByName("OtherRoom");
|
||||
const pipContainer = page.locator(".mx_WidgetPip");
|
||||
const pipContainer = page.getByTestId("widget-pip-container");
|
||||
|
||||
// We should have a PiP container here.
|
||||
await expect(pipContainer).toBeVisible();
|
||||
|
||||
// Leave the call.
|
||||
const overlay = page.locator(".mx_WidgetPip_overlay");
|
||||
await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
||||
await overlay.getByRole("button", { name: "Leave", exact: true }).click();
|
||||
const fakeWidget = page.locator('iframe[title="Element Call"]').contentFrame();
|
||||
|
||||
// await overlay.hover({ timeout: 2000 }); // Show the call footer.
|
||||
await fakeWidget.getByRole("button", { name: "Close", exact: true }).click();
|
||||
|
||||
// PiP container goes.
|
||||
await expect(pipContainer).not.toBeVisible();
|
||||
|
||||
@@ -103,7 +103,7 @@ async function expectTimelineSticker(page: Page, serverName: string, roomId: str
|
||||
}
|
||||
|
||||
async function expectFileTile(page: Page, roomId: string, contentUri: string) {
|
||||
await expect(page.locator(".mx_MFileBody_info_filename")).toContainText(STICKER_NAME);
|
||||
await expect(page.locator(".mx_MFileBody [data-type='info']")).toContainText(STICKER_NAME);
|
||||
}
|
||||
|
||||
async function setWidgetAccountData(
|
||||
|
||||
@@ -139,7 +139,7 @@ test.describe("Widget PIP", () => {
|
||||
);
|
||||
|
||||
// checks that pip window is opened
|
||||
await expect(page.locator(".mx_WidgetPip")).toBeVisible();
|
||||
await expect(page.getByTestId("widget-pip-container")).toBeVisible();
|
||||
|
||||
// checks that widget is opened in pip
|
||||
const iframe = page.frameLocator(`iframe[title="${DEMO_WIDGET_NAME}"]`);
|
||||
@@ -155,7 +155,7 @@ test.describe("Widget PIP", () => {
|
||||
}
|
||||
|
||||
// checks that pip window is closed
|
||||
await expect(iframe.locator(".mx_WidgetPip")).not.toBeVisible();
|
||||
await expect(iframe.getByTestId("widget-pip-container")).not.toBeVisible();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 263 KiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.7 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.7 KiB |
@@ -11,7 +11,7 @@ import {
|
||||
} from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js";
|
||||
|
||||
const DOCKER_IMAGE =
|
||||
"ghcr.io/element-hq/matrix-authentication-service:main@sha256:a28fb988827211b19f8983465a286291aa7112dfbe410ad24c849f74aee4ce0f";
|
||||
"ghcr.io/element-hq/matrix-authentication-service:main@sha256:baa02c35e22dec0aad82b03de3157acd33b7b727a4b5fc68a67c512f0204684e";
|
||||
|
||||
/**
|
||||
* MatrixAuthenticationServiceContainer which freezes the docker digest to
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
import { SynapseContainer as BaseSynapseContainer } from "@element-hq/element-web-playwright-common/lib/testcontainers/index.js";
|
||||
|
||||
const DOCKER_IMAGE =
|
||||
"ghcr.io/element-hq/synapse:develop@sha256:b935cb06b158966dad4faa81d021fc6c7c4080fa5361d9b3a08efc431a249a81";
|
||||
"ghcr.io/element-hq/synapse:develop@sha256:b256d748144934a03434d57ca24659b1e432cea258bf6e41caaf4d84e498bd55";
|
||||
|
||||
/**
|
||||
* SynapseContainer which freezes the docker digest to stabilise tests,
|
||||
|
||||
@@ -34,9 +34,12 @@
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"tsc --noEmit --project ./tsconfig.module_system.json",
|
||||
"tsc --noEmit",
|
||||
"tsc --noEmit -p playwright"
|
||||
// We indirect via `pnpm exec` to stop knip interpreting the
|
||||
// commandline and declaring `playwright` and `./tsconfig.module_system.json`
|
||||
// as unlisted dependencies.
|
||||
"pnpm exec tsc --noEmit --project ./tsconfig.module_system.json",
|
||||
"pnpm exec tsc --noEmit",
|
||||
"pnpm exec tsc --noEmit --project playwright"
|
||||
],
|
||||
"parallel": false,
|
||||
"cwd": "apps/web"
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
@import "./components/views/location/_ZoomButtons.pcss";
|
||||
@import "./components/views/messages/_MBeaconBody.pcss";
|
||||
@import "./components/views/messages/shared/_MediaProcessingError.pcss";
|
||||
@import "./components/views/pips/_WidgetPip.pcss";
|
||||
@import "./components/views/polls/_PollOption.pcss";
|
||||
@import "./components/views/settings/_AddRemoveThreepids.pcss";
|
||||
@import "./components/views/settings/devices/_CurrentDeviceSection.pcss";
|
||||
@@ -237,7 +236,6 @@
|
||||
@import "./views/messages/_MediaBody.pcss";
|
||||
@import "./views/messages/_MessageActionBar.pcss";
|
||||
@import "./views/messages/_MjolnirBody.pcss";
|
||||
@import "./views/messages/_PinnedMessageBadge.pcss";
|
||||
@import "./views/messages/_ReactionsRow.pcss";
|
||||
@import "./views/messages/_RedactedBody.pcss";
|
||||
@import "./views/messages/_RoomAvatarEvent.pcss";
|
||||
@@ -278,8 +276,6 @@
|
||||
@import "./views/rooms/_JumpToBottomButton.pcss";
|
||||
@import "./views/rooms/_LegacyRoomList.pcss";
|
||||
@import "./views/rooms/_LegacyRoomListHeader.pcss";
|
||||
@import "./views/rooms/_LinkPreviewGroup.pcss";
|
||||
@import "./views/rooms/_LinkPreviewWidget.pcss";
|
||||
@import "./views/rooms/_LiveContentSummary.pcss";
|
||||
@import "./views/rooms/_MemberListHeaderView.pcss";
|
||||
@import "./views/rooms/_MemberListView.pcss";
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
Copyright 2024 New Vector Ltd.
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
$width: 320px;
|
||||
$height: 220px;
|
||||
|
||||
.mx_WidgetPip {
|
||||
width: $width;
|
||||
height: $height;
|
||||
}
|
||||
|
||||
.mx_WidgetPip_overlay {
|
||||
width: $width;
|
||||
height: $height;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
color: $call-primary-content;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.mx_WidgetPip_header,
|
||||
.mx_WidgetPip_footer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
transition: opacity ease 0.15s;
|
||||
}
|
||||
|
||||
.mx_WidgetPip_overlay:not(:hover) {
|
||||
.mx_WidgetPip_header,
|
||||
.mx_WidgetPip_footer {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_WidgetPip_header {
|
||||
top: 0;
|
||||
padding: $spacing-12;
|
||||
display: flex;
|
||||
font-size: $font-12px;
|
||||
font-weight: var(--cpd-font-weight-semibold);
|
||||
background: linear-gradient(rgb(0, 0, 0, 0.9), rgb(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
.mx_WidgetPip_backButton {
|
||||
height: $spacing-24;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: $spacing-12;
|
||||
|
||||
> .mx_Icon {
|
||||
color: $call-light-quaternary-content;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_WidgetPip_footer {
|
||||
bottom: 0;
|
||||
padding: $spacing-12 $spacing-8;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: flex-end;
|
||||
background: linear-gradient(rgb(0, 0, 0, 0), rgb(0, 0, 0, 0.9));
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@import url("@vector-im/compound-design-tokens/assets/web/css/compound-design-tokens.css") layer(compound);
|
||||
@import url("@vector-im/compound-web/dist/style.css");
|
||||
@@ -41,7 +41,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
|
||||
.mx_MFileBody_download {
|
||||
.mx_MFileBody [data-type="download"] {
|
||||
margin-top: var(--cpd-space-4x);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
}
|
||||
|
||||
&:hover .mx_SpacePanel_toggleCollapse {
|
||||
&:hover .mx_SpacePanel_toggleCollapse,
|
||||
.mx_SpacePanel_toggleCollapse:focus-visible {
|
||||
opacity: 1;
|
||||
/* For enhanced visibility under contrast control */
|
||||
outline: 1px solid transparent;
|
||||
|
||||
@@ -187,3 +187,53 @@ Please see LICENSE files in the repository root for full details.
|
||||
/* used on focus */
|
||||
color: $links !important;
|
||||
}
|
||||
|
||||
.mx_DevTools_sticky_explorer {
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
margin-top: var(--cpd-space-3x);
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding: var(--cpd-space-2x) var(--cpd-space-3x);
|
||||
}
|
||||
|
||||
th#user_header {
|
||||
width: 35%;
|
||||
}
|
||||
th#sticky_key_header {
|
||||
width: 50%;
|
||||
}
|
||||
th#expires_in_header {
|
||||
width: 15%;
|
||||
}
|
||||
|
||||
tr {
|
||||
cursor: pointer;
|
||||
border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-border-interactive-primary);
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
color: var(--cpd-color-text-secondary);
|
||||
background: var(--cpd-color-bg-action-secondary-hovered);
|
||||
}
|
||||
|
||||
tr:focus-visible {
|
||||
outline: var(--cpd-border-width-2) solid var(--cpd-color-border-focused);
|
||||
}
|
||||
|
||||
td {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: var(--cpd-space-2x) var(--cpd-space-3x);
|
||||
}
|
||||
|
||||
td.remaining_time_column {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
/* We also hide download links to not encourage users to try interacting */
|
||||
.mx_EventTile_msgOption,
|
||||
.mx_EventTile_e2eIcon,
|
||||
.mx_MFileBody_download {
|
||||
.mx_MFileBody [data-type="download"] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
vertical-align: middle;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $alert;
|
||||
mask-size: 100%;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,56 +6,23 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.mx_MFileBody_download {
|
||||
.mx_MFileBody [data-type="download"] {
|
||||
color: $accent;
|
||||
height: var(--cpd-space-9x);
|
||||
}
|
||||
|
||||
.mx_MFileBody_download object {
|
||||
margin-left: -16px;
|
||||
padding-right: 4px;
|
||||
margin-top: -4px;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Remove the border and padding for iframes for download links. */
|
||||
.mx_MFileBody_download iframe {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mx_MFileBody_info {
|
||||
cursor: pointer;
|
||||
|
||||
.mx_MFileBody_info_icon {
|
||||
background-color: $system;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: var(--cpd-space-2x);
|
||||
vertical-align: middle;
|
||||
margin-right: 12px;
|
||||
|
||||
svg {
|
||||
color: $secondary-content;
|
||||
width: inherit;
|
||||
height: inherit;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MFileBody_info_filename {
|
||||
font: var(--cpd-font-body-md-regular);
|
||||
color: var(--cpd-color-text-primary);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
width: calc(100% - 32px - 12px); /* 32px icon, 12px margin on the icon */
|
||||
& object {
|
||||
margin-left: -16px;
|
||||
padding-right: 4px;
|
||||
margin-top: -4px;
|
||||
vertical-align: middle;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MFileBody [data-type="info"] {
|
||||
svg {
|
||||
color: $secondary-content !important;
|
||||
background-color: $system !important;
|
||||
border-radius: 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_MessageActionBar {
|
||||
--MessageActionBar-size-button: 28px;
|
||||
--MessageActionBar-size-box: 32px; /* 28px + 2px (margin) * 2 */
|
||||
--MessageActionBar-size-margin: 3px;
|
||||
--MessageActionBar-item-hover-background: var(--cpd-color-bg-subtle-secondary);
|
||||
--MessageActionBar-item-hover-borderRadius: 6px;
|
||||
--MessageActionBar-item-hover-zIndex: 1;
|
||||
@@ -17,12 +17,18 @@ Please see LICENSE files in the repository root for full details.
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
height: var(--MessageActionBar-size-box);
|
||||
gap: var(--cpd-space-0-5x);
|
||||
line-height: $font-24px;
|
||||
border-radius: 8px;
|
||||
background: $background;
|
||||
border: var(--cpd-border-width-1) solid var(--cpd-color-border-disabled);
|
||||
top: -32px;
|
||||
top: calc(
|
||||
-1 *
|
||||
(
|
||||
var(--MessageActionBar-size-button) + 2 *
|
||||
(var(--MessageActionBar-size-margin) + var(--cpd-border-width-1))
|
||||
)
|
||||
);
|
||||
right: 8px;
|
||||
user-select: none;
|
||||
/* Ensure the action bar appears above other things like the read marker */
|
||||
@@ -74,7 +80,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin: 2px;
|
||||
margin: var(--MessageActionBar-size-margin);
|
||||
|
||||
&:hover {
|
||||
background: var(--MessageActionBar-item-hover-background);
|
||||
@@ -84,7 +90,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
|
||||
.mx_MessageActionBar_iconButton {
|
||||
--MessageActionBar-icon-size: 18px;
|
||||
--MessageActionBar-icon-size: 20px;
|
||||
width: var(--MessageActionBar-size-button);
|
||||
height: var(--MessageActionBar-size-button);
|
||||
color: var(--cpd-color-icon-secondary);
|
||||
@@ -108,30 +114,12 @@ Please see LICENSE files in the repository root for full details.
|
||||
color: var(--cpd-color-icon-primary);
|
||||
}
|
||||
|
||||
&.mx_MessageActionBar_threadButton {
|
||||
--MessageActionBar-icon-size: 20px;
|
||||
}
|
||||
|
||||
&.mx_MessageActionBar_retryButton {
|
||||
--MessageActionBar-icon-size: 16px;
|
||||
}
|
||||
|
||||
&.mx_MessageActionBar_downloadButton {
|
||||
--MessageActionBar-icon-size: 20px;
|
||||
|
||||
&.mx_MessageActionBar_downloadSpinnerButton {
|
||||
svg {
|
||||
display: none; /* hide the download icon */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.mx_MessageActionBar_expandCollapseMessageButton {
|
||||
--MessageActionBar-icon-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.mx_MessageActionBar_optionsButton {
|
||||
--MessageActionBar-icon-size: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,29 +9,7 @@ Please see LICENSE files in the repository root for full details.
|
||||
.mx_EventTileBubble.mx_cryptoEvent {
|
||||
margin: var(--EventTileBubble_margin-block) auto;
|
||||
|
||||
&.mx_cryptoEvent_icon svg {
|
||||
svg[data-state="supported"] {
|
||||
color: $header-panel-text-primary-color;
|
||||
}
|
||||
|
||||
.mx_cryptoEvent_state,
|
||||
.mx_cryptoEvent_buttons {
|
||||
grid-column: 3;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
.mx_cryptoEvent_buttons {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.mx_cryptoEvent_state {
|
||||
width: 130px;
|
||||
padding: 10px 20px;
|
||||
margin: auto 0;
|
||||
text-align: center;
|
||||
color: $tertiary-content;
|
||||
overflow-wrap: break-word;
|
||||
font-size: $font-12px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
|
||||
Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--AppTile_mini-height: 220px;
|
||||
}
|
||||
|
||||
.mx_AppsDrawer {
|
||||
--minWidth: 240px; /* TODO this should be 300px but that's too large */
|
||||
|
||||
@@ -168,11 +164,11 @@ Please see LICENSE files in the repository root for full details.
|
||||
|
||||
.mx_AppTile_mini {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--AppTile_mini-height);
|
||||
}
|
||||
|
||||
.mx_AppTile .mx_AppTile_persistedWrapper,
|
||||
@@ -276,8 +272,8 @@ Please see LICENSE files in the repository root for full details.
|
||||
&.mx_AppTileBody--large,
|
||||
&.mx_AppTileBody--mini {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
height: var(--AppTileBody-height);
|
||||
|
||||
iframe {
|
||||
border: none;
|
||||
@@ -299,10 +295,6 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
}
|
||||
|
||||
&.mx_AppTileBody--mini {
|
||||
--AppTileBody-height: var(--AppTile_mini-height);
|
||||
}
|
||||
|
||||
&.mx_AppTileBody--loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -196,7 +196,9 @@ Please see LICENSE files in the repository root for full details.
|
||||
}
|
||||
|
||||
.mx_MessageActionBar {
|
||||
inset-inline-start: calc(100% - var(--MessageActionBar-size-box));
|
||||
inset-inline-start: calc(
|
||||
100% - var(--MessageActionBar-size-button) - 2 * var(--MessageActionBar-size-margin)
|
||||
);
|
||||
right: initial; /* Reset the default value */
|
||||
}
|
||||
|
||||
|
||||