diff --git a/.eslintignore b/.eslintignore index b715bcd4f6..5d117f54b5 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,5 +1,6 @@ -src/component-index.js test/end-to-end-tests/node_modules/ test/end-to-end-tests/element/ test/end-to-end-tests/synapse/ test/end-to-end-tests/lib/ +# Legacy skinning file that some people might still have +src/component-index.js diff --git a/.eslintrc.js b/.eslintrc.js index 9efdd12854..67e6ab1e64 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -92,6 +92,7 @@ module.exports = { files: [ "src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}", + "cypress/**/*.ts", ], extends: [ "plugin:matrix-org/typescript", diff --git a/.github/workflows/element-build-and-test.yaml b/.github/workflows/element-build-and-test.yaml new file mode 100644 index 0000000000..1633aae260 --- /dev/null +++ b/.github/workflows/element-build-and-test.yaml @@ -0,0 +1,49 @@ +# Produce a build of element-web with this version of react-sdk +# and any matching branches of element-web and js-sdk, output it +# as an artifact and run integration tests. +name: Element Web - Build and Test +on: + pull_request: +jobs: + build: + runs-on: ubuntu-latest + env: + # This must be set for fetchdep.sh to get the right branch + PR_NUMBER: ${{github.event.number}} + steps: + - uses: actions/checkout@v2 + - name: Build + run: scripts/ci/layered.sh && cd element-web && cp element.io/develop/config.json config.json && CI_PACKAGE=true yarn build + - name: Upload Artifact + uses: actions/upload-artifact@v2 + with: + name: previewbuild + path: element-web/webapp + # We'll only use this in a triggered job, then we're done with it + retention-days: 1 + cypress: + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Download build + uses: actions/download-artifact@v3 + with: + name: previewbuild + path: webapp + - name: Run Cypress tests + uses: cypress-io/github-action@v2 + with: + # The built in Electron runner seems to grind to a halt trying + # to run the tests, so use chrome. + browser: chrome + start: npx serve -p 8080 webapp + - name: Upload Artifact + if: failure() + uses: actions/upload-artifact@v2 + with: + name: cypress-results + path: | + cypress/screenshots + cypress/videos + cypress/synapselogs diff --git a/.github/workflows/layered-build.yaml b/.github/workflows/layered-build.yaml deleted file mode 100644 index 1610f0e66e..0000000000 --- a/.github/workflows/layered-build.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Produce a 'layered build' (a build of element-web with this version of -# react-sdk) and output it as an artifact -name: Layered Preview Build -on: - pull_request: -jobs: - build: - runs-on: ubuntu-latest - env: - # This must be set for fetchdep.sh to get the right branch - PR_NUMBER: ${{github.event.number}} - steps: - - uses: actions/checkout@v2 - - name: Build - run: scripts/ci/layered.sh && cd element-web && cp element.io/develop/config.json config.json && CI_PACKAGE=true yarn build - - name: Upload Artifact - uses: actions/upload-artifact@v2 - with: - name: previewbuild - path: element-web/webapp - # We'll only use this in a triggered job, then we're done with it - retention-days: 1 - diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml index 864f029d1e..ec09379b6e 100644 --- a/.github/workflows/netlify.yaml +++ b/.github/workflows/netlify.yaml @@ -3,7 +3,7 @@ name: Upload Preview Build to Netlify on: workflow_run: - workflows: ["Layered Preview Build"] + workflows: ["Element Web - Build and Test"] types: - completed jobs: diff --git a/.github/workflows/test_coverage.yml b/.github/workflows/test_coverage.yml index e01fd6ebcc..4cd9f6d2f0 100644 --- a/.github/workflows/test_coverage.yml +++ b/.github/workflows/test_coverage.yml @@ -25,10 +25,11 @@ jobs: run: "./scripts/ci/install-deps.sh --ignore-scripts" - name: Run tests with coverage - run: "yarn install && yarn reskindex && yarn coverage" + run: "yarn install && yarn coverage" - name: Upload coverage uses: codecov/codecov-action@v2 with: fail_ci_if_error: false verbose: true + override_commit: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }} diff --git a/.gitignore b/.gitignore index 102f4b5ec1..8e14ba9057 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ package-lock.json /matrix-react-sdk-*.tgz /.idea +# Legacy skinning file that some people might still have /src/component-index.js .DS_Store @@ -18,3 +19,11 @@ package-lock.json .vscode .vscode/ + +/cypress/videos +/cypress/downloads +/cypress/screenshots +/cypress/synapselogs +# These could have files in them but don't currently +# Cypress will still auto-create them though... +/cypress/fixtures diff --git a/CHANGELOG.md b/CHANGELOG.md index 62f25872ec..841a7360a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +Changes in [3.43.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.43.0) (2022-04-26) +===================================================================================================== + +## ✨ Features + * Improve performance of switching to rooms with lots of servers and ACLs ([\#8347](https://github.com/matrix-org/matrix-react-sdk/pull/8347)). + * Avoid a reflow when setting caret position on an empty composer ([\#8348](https://github.com/matrix-org/matrix-react-sdk/pull/8348)). + * Add message right-click context menu as a labs feature ([\#5672](https://github.com/matrix-org/matrix-react-sdk/pull/5672)). + * Live location sharing - basic maximised beacon map ([\#8310](https://github.com/matrix-org/matrix-react-sdk/pull/8310)). + * Live location sharing - render users own beacons in timeline ([\#8296](https://github.com/matrix-org/matrix-react-sdk/pull/8296)). + * Improve Threads beta around degraded mode ([\#8318](https://github.com/matrix-org/matrix-react-sdk/pull/8318)). + * Live location sharing - beacon in timeline happy path ([\#8285](https://github.com/matrix-org/matrix-react-sdk/pull/8285)). + * Add copy button to View Source screen ([\#8278](https://github.com/matrix-org/matrix-react-sdk/pull/8278)). Fixes vector-im/element-web#21482. Contributed by @olivialivia. + * Add heart effect ([\#6188](https://github.com/matrix-org/matrix-react-sdk/pull/6188)). Contributed by @CicadaCinema. + * Update new room icon ([\#8239](https://github.com/matrix-org/matrix-react-sdk/pull/8239)). + +## 🐛 Bug Fixes + * Fix: "Code formatting button does not escape backticks" ([\#8181](https://github.com/matrix-org/matrix-react-sdk/pull/8181)). Contributed by @yaya-usman. + * Fix beta indicator dot causing excessive CPU usage ([\#8340](https://github.com/matrix-org/matrix-react-sdk/pull/8340)). Fixes vector-im/element-web#21793. + * Fix overlapping timestamps on empty messages ([\#8205](https://github.com/matrix-org/matrix-react-sdk/pull/8205)). Fixes vector-im/element-web#21381. Contributed by @goelesha. + * Fix power selector not showing up in user info when state_default undefined ([\#8297](https://github.com/matrix-org/matrix-react-sdk/pull/8297)). Fixes vector-im/element-web#21669. + * Avoid looking up settings during timeline rendering ([\#8313](https://github.com/matrix-org/matrix-react-sdk/pull/8313)). Fixes vector-im/element-web#21740. + * Fix a soft crash with video rooms ([\#8333](https://github.com/matrix-org/matrix-react-sdk/pull/8333)). + * Fixes call tiles overflow ([\#8096](https://github.com/matrix-org/matrix-react-sdk/pull/8096)). Fixes vector-im/element-web#20254. Contributed by @luixxiul. + * Fix a bug with emoji autocomplete sorting where adding the final ":" would cause the emoji with the typed shortcode to no longer be at the top of the autocomplete list. ([\#8086](https://github.com/matrix-org/matrix-react-sdk/pull/8086)). Fixes vector-im/element-web#19302. Contributed by @commonlawfeature. + * Fix image preview sizing for edge cases ([\#8322](https://github.com/matrix-org/matrix-react-sdk/pull/8322)). Fixes vector-im/element-web#20088. + * Refactor SecurityRoomSettingsTab and remove unused state ([\#8306](https://github.com/matrix-org/matrix-react-sdk/pull/8306)). Fixes matrix-org/element-web-rageshakes#12002. + * Don't show the prompt to enable desktop notifications immediately after registration ([\#8274](https://github.com/matrix-org/matrix-react-sdk/pull/8274)). + * Stop tracking threads if threads support is disabled ([\#8308](https://github.com/matrix-org/matrix-react-sdk/pull/8308)). Fixes vector-im/element-web#21766. + * Fix some issues with threads rendering ([\#8305](https://github.com/matrix-org/matrix-react-sdk/pull/8305)). Fixes vector-im/element-web#21670. + * Fix threads rendering issue in Safari ([\#8298](https://github.com/matrix-org/matrix-react-sdk/pull/8298)). Fixes vector-im/element-web#21757. + * Fix space panel width change on hovering over space item ([\#8299](https://github.com/matrix-org/matrix-react-sdk/pull/8299)). Fixes vector-im/element-web#19891. + * Hide the reply in thread button in deployments where beta is forcibly disabled ([\#8294](https://github.com/matrix-org/matrix-react-sdk/pull/8294)). Fixes vector-im/element-web#21753. + * Prevent soft crash around room list header context menu when space changes ([\#8289](https://github.com/matrix-org/matrix-react-sdk/pull/8289)). Fixes matrix-org/element-web-rageshakes#11416, matrix-org/element-web-rageshakes#11692, matrix-org/element-web-rageshakes#11739, matrix-org/element-web-rageshakes#11772, matrix-org/element-web-rageshakes#11891 matrix-org/element-web-rageshakes#11858 and matrix-org/element-web-rageshakes#11456. + * When selecting reply in thread on a thread response open existing thread ([\#8291](https://github.com/matrix-org/matrix-react-sdk/pull/8291)). Fixes vector-im/element-web#21743. + * Handle thread bundled relationships coming from the server via MSC3666 ([\#8292](https://github.com/matrix-org/matrix-react-sdk/pull/8292)). Fixes vector-im/element-web#21450. + * Fix: Avatar preview does not update when same file is selected repeatedly ([\#8288](https://github.com/matrix-org/matrix-react-sdk/pull/8288)). Fixes vector-im/element-web#20098. + * Fix a bug where user gets a warning when changing powerlevel from **Admin** to **custom level (100)** ([\#8248](https://github.com/matrix-org/matrix-react-sdk/pull/8248)). Fixes vector-im/element-web#21682. Contributed by @Jumeb. + * Use a consistent alignment for all text items in a list ([\#8276](https://github.com/matrix-org/matrix-react-sdk/pull/8276)). Fixes vector-im/element-web#21731. Contributed by @luixxiul. + * Fixes button labels being collapsed per a character in CJK languages ([\#8212](https://github.com/matrix-org/matrix-react-sdk/pull/8212)). Fixes vector-im/element-web#21287. Contributed by @luixxiul. + * Fix: Remove jittery timeline scrolling after jumping to an event ([\#8263](https://github.com/matrix-org/matrix-react-sdk/pull/8263)). + * Fix regression of edits showing up in the timeline with hidden events shown ([\#8260](https://github.com/matrix-org/matrix-react-sdk/pull/8260)). Fixes vector-im/element-web#21694. + * Fix reporting events not working ([\#8257](https://github.com/matrix-org/matrix-react-sdk/pull/8257)). Fixes vector-im/element-web#21713. + * Make Jitsi widgets in video rooms immutable ([\#8244](https://github.com/matrix-org/matrix-react-sdk/pull/8244)). Fixes vector-im/element-web#21647. + * Fix: Ensure links to events scroll the correct events into view ([\#8250](https://github.com/matrix-org/matrix-react-sdk/pull/8250)). Fixes vector-im/element-web#19934. + Changes in [3.42.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.42.4) (2022-04-14) ===================================================================================================== @@ -483,7 +528,7 @@ Changes in [3.39.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/ ===================================================================================================== ## 🐛 Bug Fixes - * Fix the sticker picker ([\#7692](https://github.com/matrix-org/matrix-react-sdk/pull/7692)). Fixes vector-im/element-web#20797. + * Fix the sticker picker ([\#7692](https://github.com/matrix-org/matrix-react-sdk/pull/7692)). Fixes vector-im/element-web#20797. * Ensure UserInfo can be rendered without a room ([\#7687](https://github.com/matrix-org/matrix-react-sdk/pull/7687)). Fixes vector-im/element-web#20830. * Fix publishing address wrongly demanding the alias be available ([\#7690](https://github.com/matrix-org/matrix-react-sdk/pull/7690)). Fixes vector-im/element-web#12013 and vector-im/element-web#20833. @@ -1342,7 +1387,7 @@ Changes in [3.32.1](https://github.com/vector-im/element-desktop/releases/tag/v3 ## 🐛 Bug Fixes * Upgrade to matrix-js-sdk#14.0.1 - + Changes in [3.32.0](https://github.com/vector-im/element-desktop/releases/tag/v3.32.0) (2021-10-11) =================================================================================================== @@ -2488,7 +2533,7 @@ related to file upload. When uploading a file, the local file preview can lead to execution of scripts embedded in the uploaded file, but only after several user interactions to open the preview in a separate tab. This only impacts the local user while in the process of uploading. It cannot be exploited remotely -or by other users. Thanks to [Muhammad Zaid Ghifari](https://github.com/MR-ZHEEV) +or by other users. Thanks to [Muhammad Zaid Ghifari](https://github.com/MR-ZHEEV) for responsibly disclosing this via Matrix's Security Disclosure Policy. ## All changes @@ -6483,7 +6528,7 @@ Changes in [2.1.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/ ============================================================================================================= [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v2.1.0-rc.1...v2.1.0-rc.2) - * Fix error in previous attempt to upgrade JS SDK + * Fix error in previous attempt to upgrade JS SDK Changes in [2.1.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v2.1.0-rc.1) (2020-02-13) ============================================================================================================= diff --git a/README.md b/README.md index 7044bb9681..4664887360 100644 --- a/README.md +++ b/README.md @@ -65,13 +65,9 @@ practices that anyone working with the SDK needs to be aware of and uphold: component is a view or a structure, and then a broad functional grouping (e.g. 'rooms' here) - * After creating a new component you must run `yarn reskindex` to regenerate - the `component-index.js` for the SDK (used in future for skinning) - - * The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css). CSS for matrix-react-sdk currently resides in - https://github.com/vector-im/element-web/tree/master/src/skins/vector/css/matrix-react-sdk. + https://github.com/matrix-org/matrix-react-sdk/tree/master/res/css. * Per-view CSS is optional - it could choose to inherit all its styling from the context of the rest of the app, although this is unusual for any but @@ -158,9 +154,6 @@ cd matrix-react-sdk git checkout develop yarn link matrix-js-sdk yarn install - -# Generate the `component-index.js` file. -yarn reskindex ``` See the [help for `yarn link`](https://classic.yarnpkg.com/docs/cli/link) for diff --git a/__mocks__/maplibre-gl.js b/__mocks__/maplibre-gl.js index 8686089825..599cacde13 100644 --- a/__mocks__/maplibre-gl.js +++ b/__mocks__/maplibre-gl.js @@ -1,9 +1,14 @@ const EventEmitter = require("events"); -const { LngLat, NavigationControl } = require('maplibre-gl'); +const { LngLat, NavigationControl, LngLatBounds } = require('maplibre-gl'); class MockMap extends EventEmitter { addControl = jest.fn(); removeControl = jest.fn(); + zoomIn = jest.fn(); + zoomOut = jest.fn(); + setCenter = jest.fn(); + setStyle = jest.fn(); + fitBounds = jest.fn(); } const MockMapInstance = new MockMap(); @@ -14,10 +19,12 @@ const MockGeolocateInstance = new MockGeolocateControl(); const MockMarker = {} MockMarker.setLngLat = jest.fn().mockReturnValue(MockMarker); MockMarker.addTo = jest.fn().mockReturnValue(MockMarker); +MockMarker.remove = jest.fn().mockReturnValue(MockMarker); module.exports = { Map: jest.fn().mockReturnValue(MockMapInstance), GeolocateControl: jest.fn().mockReturnValue(MockGeolocateInstance), Marker: jest.fn().mockReturnValue(MockMarker), LngLat, - NavigationControl + LngLatBounds, + NavigationControl, }; diff --git a/babel.config.js b/babel.config.js index f00e83652c..ac94a29559 100644 --- a/babel.config.js +++ b/babel.config.js @@ -13,7 +13,6 @@ module.exports = { "@babel/preset-react", ], "plugins": [ - ["@babel/plugin-proposal-decorators", {legacy: true}], "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-numeric-separator", "@babel/plugin-proposal-class-properties", diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000000..4c1ed2d585 --- /dev/null +++ b/cypress.json @@ -0,0 +1,4 @@ +{ + "baseUrl": "http://localhost:8080", + "videoUploadOnPasses": false +} diff --git a/cypress/integration/1-register/register.spec.ts b/cypress/integration/1-register/register.spec.ts new file mode 100644 index 0000000000..f719da5547 --- /dev/null +++ b/cypress/integration/1-register/register.spec.ts @@ -0,0 +1,52 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import { SynapseInstance } from "../../plugins/synapsedocker/index"; + +describe("Registration", () => { + let synapseId; + let synapsePort; + + beforeEach(() => { + cy.task("synapseStart", "consent").then(result => { + synapseId = result.synapseId; + synapsePort = result.port; + }); + cy.visit("/#/register"); + }); + + afterEach(() => { + cy.task("synapseStop", synapseId); + }); + + it("registers an account and lands on the home screen", () => { + cy.get(".mx_ServerPicker_change", { timeout: 15000 }).click(); + cy.get(".mx_ServerPickerDialog_otherHomeserver").type(`http://localhost:${synapsePort}`); + cy.get(".mx_ServerPickerDialog_continue").click(); + // wait for the dialog to go away + cy.get('.mx_ServerPickerDialog').should('not.exist'); + cy.get("#mx_RegistrationForm_username").type("alice"); + cy.get("#mx_RegistrationForm_password").type("totally a great password"); + cy.get("#mx_RegistrationForm_passwordConfirm").type("totally a great password"); + cy.get(".mx_Login_submit").click(); + cy.get(".mx_RegistrationEmailPromptDialog button.mx_Dialog_primary").click(); + cy.get(".mx_InteractiveAuthEntryComponents_termsPolicy input").click(); + cy.get(".mx_InteractiveAuthEntryComponents_termsSubmit").click(); + cy.url().should('contain', '/#/home'); + }); +}); diff --git a/test/minimal-sdk.js b/cypress/plugins/index.ts similarity index 74% rename from test/minimal-sdk.js rename to cypress/plugins/index.ts index f39893f78c..db01ceceb4 100644 --- a/test/minimal-sdk.js +++ b/cypress/plugins/index.ts @@ -14,16 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* - * minimal-sdk.js - * - * Starts up the skin system with even less than `skinned-sdk`. - */ +/// -import * as sdk from "../src/index"; +import { synapseDocker } from "./synapsedocker/index"; -const components = {}; - -sdk.loadSkin({ components }); - -export default sdk; +export default function(on, config) { + synapseDocker(on, config); +} diff --git a/cypress/plugins/synapsedocker/index.ts b/cypress/plugins/synapsedocker/index.ts new file mode 100644 index 0000000000..0f029e7b2e --- /dev/null +++ b/cypress/plugins/synapsedocker/index.ts @@ -0,0 +1,212 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/// + +import * as path from "path"; +import * as os from "os"; +import * as crypto from "crypto"; +import * as childProcess from "child_process"; +import * as fse from "fs-extra"; + +// A cypress plugins to add command to start & stop synapses in +// docker with preset templates. + +interface SynapseConfig { + configDir: string; + registrationSecret: string; +} + +export interface SynapseInstance extends SynapseConfig { + synapseId: string; + port: number; +} + +const synapses = new Map(); + +function randB64Bytes(numBytes: number): string { + return crypto.randomBytes(numBytes).toString("base64").replace(/=*$/, ""); +} + +async function cfgDirFromTemplate(template: string): Promise { + const templateDir = path.join(__dirname, "templates", template); + + const stats = await fse.stat(templateDir); + if (!stats?.isDirectory) { + throw new Error(`No such template: ${template}`); + } + const tempDir = await fse.mkdtemp(path.join(os.tmpdir(), 'react-sdk-synapsedocker-')); + + // change permissions on the temp directory so the docker container can see its contents + await fse.chmod(tempDir, 0o777); + + // copy the contents of the template dir, omitting homeserver.yaml as we'll template that + console.log(`Copy ${templateDir} -> ${tempDir}`); + await fse.copy(templateDir, tempDir, { filter: f => path.basename(f) !== 'homeserver.yaml' }); + + const registrationSecret = randB64Bytes(16); + const macaroonSecret = randB64Bytes(16); + const formSecret = randB64Bytes(16); + + // now copy homeserver.yaml, applying sustitutions + console.log(`Gen ${path.join(templateDir, "homeserver.yaml")}`); + let hsYaml = await fse.readFile(path.join(templateDir, "homeserver.yaml"), "utf8"); + hsYaml = hsYaml.replace(/{{REGISTRATION_SECRET}}/g, registrationSecret); + hsYaml = hsYaml.replace(/{{MACAROON_SECRET_KEY}}/g, macaroonSecret); + hsYaml = hsYaml.replace(/{{FORM_SECRET}}/g, formSecret); + await fse.writeFile(path.join(tempDir, "homeserver.yaml"), hsYaml); + + // now generate a signing key (we could use synapse's config generation for + // this, or we could just do this...) + // NB. This assumes the homeserver.yaml specifies the key in this location + const signingKey = randB64Bytes(32); + console.log(`Gen ${path.join(templateDir, "localhost.signing.key")}`); + await fse.writeFile(path.join(tempDir, "localhost.signing.key"), `ed25519 x ${signingKey}`); + + return { + configDir: tempDir, + registrationSecret, + }; +} + +// Start a synapse instance: the template must be the name of +// one of the templates in the cypress/plugins/synapsedocker/templates +// directory +async function synapseStart(template: string): Promise { + const synCfg = await cfgDirFromTemplate(template); + + console.log(`Starting synapse with config dir ${synCfg.configDir}...`); + + const containerName = `react-sdk-cypress-synapse-${crypto.randomBytes(4).toString("hex")}`; + + const synapseId = await new Promise((resolve, reject) => { + childProcess.execFile('docker', [ + "run", + "--name", containerName, + "-d", + "-v", `${synCfg.configDir}:/data`, + "-p", "8008/tcp", + "matrixdotorg/synapse:develop", + "run", + ], (err, stdout) => { + if (err) reject(err); + resolve(stdout.trim()); + }); + }); + + // Get the port that docker allocated: specifying only one + // port above leaves docker to just grab a free one, although + // in hindsight we need to put the port in public_baseurl in the + // config really, so this will probably need changing to use a fixed + // / configured port. + const port = await new Promise((resolve, reject) => { + childProcess.execFile('docker', [ + "port", synapseId, "8008", + ], { encoding: 'utf8' }, (err, stdout) => { + if (err) reject(err); + resolve(Number(stdout.trim().split(":")[1])); + }); + }); + + synapses.set(synapseId, Object.assign({ + port, + synapseId, + }, synCfg)); + + console.log(`Started synapse with id ${synapseId} on port ${port}.`); + return synapses.get(synapseId); +} + +async function synapseStop(id) { + const synCfg = synapses.get(id); + + if (!synCfg) throw new Error("Unknown synapse ID"); + + try { + const synapseLogsPath = path.join("cypress", "synapselogs", id); + await fse.ensureDir(synapseLogsPath); + + const stdoutFile = await fse.open(path.join(synapseLogsPath, "stdout.log"), "w"); + const stderrFile = await fse.open(path.join(synapseLogsPath, "stderr.log"), "w"); + await new Promise((resolve, reject) => { + childProcess.spawn('docker', [ + "logs", + id, + ], { + stdio: ["ignore", stdoutFile, stderrFile], + }).once('close', resolve); + }); + await fse.close(stdoutFile); + await fse.close(stderrFile); + + await new Promise((resolve, reject) => { + childProcess.execFile('docker', [ + "stop", + id, + ], err => { + if (err) reject(err); + resolve(); + }); + }); + } finally { + await new Promise((resolve, reject) => { + childProcess.execFile('docker', [ + "rm", + id, + ], err => { + if (err) reject(err); + resolve(); + }); + }); + } + + await fse.remove(synCfg.configDir); + + synapses.delete(id); + + console.log(`Stopped synapse id ${id}.`); + // cypres deliberately fails if you return 'undefined', so + // return null to signal all is well and we've handled the task. + return null; +} + +/** + * @type {Cypress.PluginConfig} + */ +// eslint-disable-next-line no-unused-vars +export function synapseDocker(on, config) { + on("task", { + synapseStart, synapseStop, + }); + + on("after:spec", async (spec) => { + // Cleans up any remaining synapse instances after a spec run + // This is on the theory that we should avoid re-using synapse + // instances between spec runs: they should be cheap enough to + // start that we can have a separate one for each spec run or even + // test. If we accidentally re-use synapses, we could inadvertantly + // make our tests depend on each other. + for (const synId of synapses.keys()) { + console.warn(`Cleaning up synapse ID ${synId} after ${spec.name}`); + await synapseStop(synId); + } + }); + + on("before:run", async () => { + // tidy up old synapse log files before each run + await fse.emptyDir(path.join("cypress", "synapselogs")); + }); +} diff --git a/cypress/plugins/synapsedocker/templates/COPYME/README.md b/cypress/plugins/synapsedocker/templates/COPYME/README.md new file mode 100644 index 0000000000..df1ed89e6e --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/COPYME/README.md @@ -0,0 +1,3 @@ +# Meta-template for synapse templates + +To make another template, you can copy this directory diff --git a/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml b/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml new file mode 100644 index 0000000000..fab1bc1c45 --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/COPYME/homeserver.yaml @@ -0,0 +1,72 @@ +server_name: "localhost" +pid_file: /data/homeserver.pid +# XXX: This won't actually be right: it lets docker allocate an ephemeral port, +# so we have a chicken-and-egg problem +public_baseurl: http://localhost:8008/ +# Listener is always port 8008 (configured in the container) +listeners: + - port: 8008 + tls: false + bind_addresses: ['::'] + type: http + x_forwarded: true + + resources: + - names: [client, federation, consent] + compress: false + +# An sqlite in-memory database is fast & automatically wipes each time +database: + name: "sqlite3" + args: + database: ":memory:" + +# Needs to be configured to log to the console like a good docker process +log_config: "/data/log.config" + +rc_messages_per_second: 10000 +rc_message_burst_count: 10000 +rc_registration: + per_second: 10000 + burst_count: 10000 + +rc_login: + address: + per_second: 10000 + burst_count: 10000 + account: + per_second: 10000 + burst_count: 10000 + failed_attempts: + per_second: 10000 + burst_count: 10000 + +media_store_path: "/data/media_store" +uploads_path: "/data/uploads" +enable_registration: true +enable_registration_without_verification: true +disable_msisdn_registration: false +# These placeholders will be be replaced with values generated at start +registration_shared_secret: "{{REGISTRATION_SECRET}}" +report_stats: false +macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" +form_secret: "{{FORM_SECRET}}" +# Signing key must be here: it will be generated to this file +signing_key_path: "/data/localhost.signing.key" +email: + enable_notifs: false + smtp_host: "localhost" + smtp_port: 25 + smtp_user: "exampleusername" + smtp_pass: "examplepassword" + require_transport_security: False + notif_from: "Your Friendly %(app)s homeserver " + app_name: Matrix + notif_template_html: notif_mail.html + notif_template_text: notif_mail.txt + notif_for_new_users: True + client_base_url: "http://localhost/element" + +trusted_key_servers: + - server_name: "matrix.org" +suppress_key_server_warning: true diff --git a/cypress/plugins/synapsedocker/templates/COPYME/log.config b/cypress/plugins/synapsedocker/templates/COPYME/log.config new file mode 100644 index 0000000000..ac232762da --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/COPYME/log.config @@ -0,0 +1,50 @@ +# Log configuration for Synapse. +# +# This is a YAML file containing a standard Python logging configuration +# dictionary. See [1] for details on the valid settings. +# +# Synapse also supports structured logging for machine readable logs which can +# be ingested by ELK stacks. See [2] for details. +# +# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema +# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html + +version: 1 + +formatters: + precise: + format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' + +handlers: + # A handler that writes logs to stderr. Unused by default, but can be used + # instead of "buffer" and "file" in the logger handlers. + console: + class: logging.StreamHandler + formatter: precise + +loggers: + synapse.storage.SQL: + # beware: increasing this to DEBUG will make synapse log sensitive + # information such as access tokens. + level: INFO + + twisted: + # We send the twisted logging directly to the file handler, + # to work around https://github.com/matrix-org/synapse/issues/3471 + # when using "buffer" logger. Use "console" to log to stderr instead. + handlers: [console] + propagate: false + +root: + level: INFO + + # Write logs to the `buffer` handler, which will buffer them together in memory, + # then write them to a file. + # + # Replace "buffer" with "console" to log to stderr instead. (Note that you'll + # also need to update the configuration for the `twisted` logger above, in + # this case.) + # + handlers: [console] + +disable_existing_loggers: false diff --git a/cypress/plugins/synapsedocker/templates/consent/README.md b/cypress/plugins/synapsedocker/templates/consent/README.md new file mode 100644 index 0000000000..713e55f9d5 --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/consent/README.md @@ -0,0 +1 @@ +A synapse configured with user privacy consent enabled diff --git a/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml b/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml new file mode 100644 index 0000000000..e26133f6d1 --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/consent/homeserver.yaml @@ -0,0 +1,84 @@ +server_name: "localhost" +pid_file: /data/homeserver.pid +public_baseurl: http://localhost:5005/ +listeners: + - port: 8008 + tls: false + bind_addresses: ['::'] + type: http + x_forwarded: true + + resources: + - names: [client, federation, consent] + compress: false + +database: + name: "sqlite3" + args: + database: ":memory:" + +log_config: "/data/log.config" + +rc_messages_per_second: 10000 +rc_message_burst_count: 10000 +rc_registration: + per_second: 10000 + burst_count: 10000 + +rc_login: + address: + per_second: 10000 + burst_count: 10000 + account: + per_second: 10000 + burst_count: 10000 + failed_attempts: + per_second: 10000 + burst_count: 10000 + +media_store_path: "/data/media_store" +uploads_path: "/data/uploads" +enable_registration: true +enable_registration_without_verification: true +disable_msisdn_registration: false +registration_shared_secret: "{{REGISTRATION_SECRET}}" +report_stats: false +macaroon_secret_key: "{{MACAROON_SECRET_KEY}}" +form_secret: "{{FORM_SECRET}}" +signing_key_path: "/data/localhost.signing.key" +email: + enable_notifs: false + smtp_host: "localhost" + smtp_port: 25 + smtp_user: "exampleusername" + smtp_pass: "examplepassword" + require_transport_security: False + notif_from: "Your Friendly %(app)s homeserver " + app_name: Matrix + notif_template_html: notif_mail.html + notif_template_text: notif_mail.txt + notif_for_new_users: True + client_base_url: "http://localhost/element" + +user_consent: + template_dir: /data/res/templates/privacy + version: 1.0 + server_notice_content: + msgtype: m.text + body: >- + To continue using this homeserver you must review and agree to the + terms and conditions at %(consent_uri)s + send_server_notice_to_guests: True + block_events_error: >- + To continue using this homeserver you must review and agree to the + terms and conditions at %(consent_uri)s + require_at_registration: true + +server_notices: + system_mxid_localpart: notices + system_mxid_display_name: "Server Notices" + system_mxid_avatar_url: "mxc://localhost:5005/oumMVlgDnLYFaPVkExemNVVZ" + room_name: "Server Notices" +trusted_key_servers: + - server_name: "matrix.org" +suppress_key_server_warning: true diff --git a/cypress/plugins/synapsedocker/templates/consent/log.config b/cypress/plugins/synapsedocker/templates/consent/log.config new file mode 100644 index 0000000000..ac232762da --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/consent/log.config @@ -0,0 +1,50 @@ +# Log configuration for Synapse. +# +# This is a YAML file containing a standard Python logging configuration +# dictionary. See [1] for details on the valid settings. +# +# Synapse also supports structured logging for machine readable logs which can +# be ingested by ELK stacks. See [2] for details. +# +# [1]: https://docs.python.org/3.7/library/logging.config.html#configuration-dictionary-schema +# [2]: https://matrix-org.github.io/synapse/latest/structured_logging.html + +version: 1 + +formatters: + precise: + format: '%(asctime)s - %(name)s - %(lineno)d - %(levelname)s - %(request)s - %(message)s' + +handlers: + # A handler that writes logs to stderr. Unused by default, but can be used + # instead of "buffer" and "file" in the logger handlers. + console: + class: logging.StreamHandler + formatter: precise + +loggers: + synapse.storage.SQL: + # beware: increasing this to DEBUG will make synapse log sensitive + # information such as access tokens. + level: INFO + + twisted: + # We send the twisted logging directly to the file handler, + # to work around https://github.com/matrix-org/synapse/issues/3471 + # when using "buffer" logger. Use "console" to log to stderr instead. + handlers: [console] + propagate: false + +root: + level: INFO + + # Write logs to the `buffer` handler, which will buffer them together in memory, + # then write them to a file. + # + # Replace "buffer" with "console" to log to stderr instead. (Note that you'll + # also need to update the configuration for the `twisted` logger above, in + # this case.) + # + handlers: [console] + +disable_existing_loggers: false diff --git a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html new file mode 100644 index 0000000000..d4959b4bcb --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/1.0.html @@ -0,0 +1,23 @@ + + + + Test Privacy policy + + + {% if has_consented %} +

+ Thank you, you've already accepted the license. +

+ {% else %} +

+ Please accept the license! +

+
+ + + + +
+ {% endif %} + + \ No newline at end of file diff --git a/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html new file mode 100644 index 0000000000..abe27d87ca --- /dev/null +++ b/cypress/plugins/synapsedocker/templates/consent/res/templates/privacy/en/success.html @@ -0,0 +1,9 @@ + + + + Test Privacy policy + + +

Danke schon

+ + \ No newline at end of file diff --git a/cypress/support/index.ts b/cypress/support/index.ts new file mode 100644 index 0000000000..9901ef4cb8 --- /dev/null +++ b/cypress/support/index.ts @@ -0,0 +1,3 @@ +// Empty file to prevent cypress from recreating a helpful example +// file on every run (their example file doesn't use semicolons and +// so fails our lint rules). diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 0000000000..85239e1a2a --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "es2016", + "lib": ["es2020", "dom"], + "types": ["cypress"], + "moduleResolution": "node" + }, + "include": ["**/*.ts"] +} diff --git a/docs/skinning.md b/docs/skinning.md index 229bc78372..0186186c24 100644 --- a/docs/skinning.md +++ b/docs/skinning.md @@ -1,71 +1,18 @@ # Skinning -The react-sdk can be skinned to replace presentation components, CSS, or -other relevant parts of the SDK. Typically consumers will replace entire -components and get the ability for custom CSS as a result. +Skinning in the context of the react-sdk is component replacement rather than CSS. This means you can override (replace) +any accessible component in the project to implement custom behaviour, look & feel, etc. Depending on your approach, +overriding CSS classes to apply custom styling is also possible, though harder to do. -This doc isn't exhaustive on how skinning works, though it should cover -some of the more complicated parts such as component replacement. +At present, the react-sdk offers no stable interface for components - this means properties and state can and do change +at any time without notice. Once we determine the react-sdk to be stable enough to use as a proper SDK, we will adjust +this policy. In the meantime, skinning is done completely at your own risk. -## Loading a skin +The approach you take is up to you - we suggest using a module replacement plugin, as found in +[webpack](https://webpack.js.org/plugins/normal-module-replacement-plugin/), though you're free to use whichever build +system works for you. The react-sdk does not have any particular functions to call to load skins, so simply replace or +extend the components/stores/etc you're after and build. As a reminder though, this is done completely at your own risk +as we cannot guarantee a stable interface at this time. -1. Generate a `component-index.js` (preferably using the tools that the react-sdk -exposes). This can typically be done with a npm script like `"reskindex -h src/header"`. -2. In your app's entry point, add something like this code: - ```javascript - import {loadSkin} from "matrix-react-sdk"; - loadSkin(import("component-index").components); - // The rest of your imports go under this. - ``` -3. Import the remainder of the SDK and bootstrap your app. - -It is extremely important that you **do not** import anything else from the -SDK prior to loading your skin as otherwise the skin might not work. Loading -the skin should be one of the first things your app does, if not the very -first thing. - -Additionally, **do not** provide `loadSkin` with the react-sdk components -themselves otherwise the app might explode. The SDK is already aware of its -components and doesn't need to be told. - -## Replacing components - -Components that replace the react-sdk ones MUST have a `replaces` static -key on the component's class to describe which component it overrides. For -example, if your `VectorAuthPage` component is meant to replace the react-sdk -`AuthPage` component then you'd add `static replaces = 'views.auth.AuthPage';` -to the `VectorAuthPage` class. - -Other than that, the skin just needs to be loaded normally as mentioned above. -Consumers of the SDK likely will not be interested in the rest of this section. - -### SDK developer notes - -Components in the react-sdk MUST be decorated with the `@replaceableComponent` -function. For components that can't use the decorator, they must use a -variation that provides similar functionality. The decorator gives consumers -an opportunity to load skinned components by abusing import ordering and -behaviour. - -Decorators are executed at import time which is why we can abuse the import -ordering behaviour: importing `loadSkin` doesn't trigger any components to -be imported, allowing the consumer to specify a skin. When the consumer does -import a component (for example, `MatrixChat`), it starts to pull in all the -components via `import` statements. When the components get pulled in the -decorator checks with the skinned components to see if it should be replacing -the component being imported. The decorator then effectively replaces the -components when needed by specifying the skinned component as an override for -the SDK's component, which should in theory override critical functions like -`render()` and lifecycle event handlers. - -The decorator also means that older usage of `getComponent()` is no longer -required because components should be replaced by the decorator. Eventually -the react-sdk should only have one usage of `getComponent()`: the decorator. - -The decorator assumes that if `getComponent()` returns null that there is -no skinned version of the component and continues on using the SDK's component. -In previous versions of the SDK, the function would throw an error instead -because it also expected the skin to list the SDK's components as well, however -that is no longer possible due to the above. - -In short, components should always be `import`ed. +Taking a look at [element-web](https://github.com/vector-im/element-web)'s approach to skinning may be worthwhile, as it +overrides some relatively simple components. diff --git a/docs/slate-formats.md b/docs/slate-formats.md deleted file mode 100644 index 7bb2fc9c5f..0000000000 --- a/docs/slate-formats.md +++ /dev/null @@ -1,88 +0,0 @@ -Guide to data types used by the Slate-based Rich Text Editor ------------------------------------------------------------- - -We always store the Slate editor state in its Value form. - -The schema for the Value is the same whether the editor is in MD or rich text mode, and is currently (rather arbitrarily) -dictated by the schema expected by slate-md-serializer, simply because it was the only bit of the pipeline which -has opinions on the schema. (slate-html-serializer lets you define how to serialize whatever schema you like). - -The BLOCK_TAGS and MARK_TAGS give the mapping from HTML tags to the schema's node types (for blocks, which describe -block content like divs, and marks, which describe inline formatted sections like spans). - -We use

as the parent tag for the message (XXX: although some tags are technically not allowed to be nested within p's) - -Various conversions are performed as content is moved between HTML, MD, and plaintext representations of HTML and MD. - -The primitives used are: - - * Markdown.js - models commonmark-formatted MD strings (as entered by the composer in MD mode) - * toHtml() - renders them to HTML suitable for sending on the wire - * isPlainText() - checks whether the parsed MD contains anything other than simple text. - * toPlainText() - renders MD to plain text in order to remove backslashes. Only works if the MD is already plaintext (otherwise it just emits HTML) - - * slate-html-serializer - * converts Values to HTML (serialising) using our schema rules - * converts HTML to Values (deserialising) using our schema rules - - * slate-md-serializer - * converts rich Values to MD strings (serialising) but using a non-commonmark generic MD dialect. - * This should use commonmark, but we use the serializer here for expedience rather than writing a commonmark one. - - * slate-plain-serializer - * converts Values to plain text strings (serialising them) by concatenating the strings together - * converts Values from plain text strings (deserialiasing them). - * Used to initialise the editor by deserializing "" into a Value. Apparently this is the idiomatic way to initialise a blank editor. - * Used (as a bodge) to turn a rich text editor into a MD editor, when deserialising the converted MD string of the editor into a value - - * PlainWithPillsSerializer - * A fork of slate-plain-serializer which is aware of Pills (hence the name) and Emoji. - * It can be configured to output Pills as: - * "plain": Pills are rendered via their 'completion' text - e.g. 'Matthew'; used for sending messages) - * "md": Pills are rendered as MD, e.g. [Matthew](https://matrix.to/#/@matthew:matrix.org) ) - * "id": Pills are rendered as IDs, e.g. '@matthew:matrix.org' (used for authoring / commands) - * Emoji nodes are converted to inline utf8 emoji. - -The actual conversion transitions are: - - * Quoting: - * The message being quoted is taken as HTML - * ...and deserialised into a Value - * ...and then serialised into MD via slate-md-serializer if the editor is in MD mode - - * Roundtripping between MD and rich text editor mode - * From MD to richtext (mdToRichEditorState): - * Serialise the MD-format Value to a MD string (converting pills to MD) with PlainWithPillsSerializer in 'md' mode - * Convert that MD string to HTML via Markdown.js - * Deserialise that Value to HTML via slate-html-serializer - * From richtext to MD (richToMdEditorState): - * Serialise the richtext-format Value to a MD string with slate-md-serializer (XXX: this should use commonmark) - * Deserialise that to a plain text value via slate-plain-serializer - - * Loading history in one format into an editor which is in the other format - * Uses the same functions as for roundtripping - - * Scanning the editor for a slash command - * If the editor is a single line node starting with /, then serialize it to a string with PlainWithPillsSerializer in 'id' mode - So that pills get converted to IDs suitable for commands being passed around - - * Sending messages - * In RT mode: - * If there is rich content, serialize the RT-format Value to HTML body via slate-html-serializer - * Serialize the RT-format Value to the plain text fallback via PlainWithPillsSerializer in 'plain' mode - * In MD mode: - * Serialize the MD-format Value into an MD string with PlainWithPillsSerializer in 'md' mode - * Parse the string with Markdown.js - * If it contains no formatting: - * Send as plaintext (as taken from Markdown.toPlainText()) - * Otherwise - * Send as HTML (as taken from Markdown.toHtml()) - * Serialize the RT-format Value to the plain text fallback via PlainWithPillsSerializer in 'plain' mode - - * Pasting HTML - * Deserialize HTML to a RT Value via slate-html-serializer - * In RT mode, insert it straight into the editor as a fragment - * In MD mode, serialise it to an MD string via slate-md-serializer and then insert the string into the editor as a fragment. - -The various scenarios and transitions could be drawn into a pretty diagram if one felt the urge, but hopefully the above -gives sufficient detail on how it's all meant to work. \ No newline at end of file diff --git a/package.json b/package.json index 82f4116272..d132189054 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.42.4", + "version": "3.43.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -22,9 +22,6 @@ "README.md", "package.json" ], - "bin": { - "reskindex": "scripts/reskindex.js" - }, "main": "./lib/index.ts", "matrix_src_main": "./src/index.ts", "matrix_lib_main": "./lib/index.ts", @@ -37,23 +34,23 @@ "i18n": "matrix-gen-i18n", "prunei18n": "matrix-prune-i18n", "diff-i18n": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && matrix-gen-i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json", - "reskindex": "node scripts/reskindex.js -h header", "make-component": "node scripts/make-react-component.js", - "reskindex:watch": "node scripts/reskindex.js -h header -w", "rethemendex": "res/css/rethemendex.sh", "clean": "rimraf lib", "build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types", - "build:compile": "yarn reskindex && babel -d lib --verbose --extensions \".ts,.js,.tsx\" src", + "build:compile": "babel -d lib --verbose --extensions \".ts,.js,.tsx\" src", "build:types": "tsc --emitDeclarationOnly --jsx react", "start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all", - "start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn start:build\" \"yarn reskindex:watch\"", + "start:all": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:build", "start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"", "lint": "yarn lint:types && yarn lint:js && yarn lint:style", - "lint:js": "eslint --max-warnings 0 src test", - "lint:js-fix": "eslint --fix src test", - "lint:types": "tsc --noEmit --jsx react", + "lint:js": "eslint --max-warnings 0 src test cypress", + "lint:js-fix": "eslint --fix src test cypress", + "lint:types": "tsc --noEmit --jsx react && tsc --noEmit -p cypress", "lint:style": "stylelint \"res/css/**/*.scss\"", "test": "jest", + "test:cypress": "cypress run", + "test:cypress:open": "cypress open", "test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080", "coverage": "yarn test --coverage" }, @@ -64,7 +61,6 @@ "@types/geojson": "^7946.0.8", "await-lock": "^2.1.0", "blurhash": "^1.1.3", - "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", "cheerio": "^1.0.0-rc.9", "classnames": "^2.2.6", @@ -93,8 +89,9 @@ "lodash": "^4.17.20", "maplibre-gl": "^1.15.2", "matrix-analytics-events": "github:matrix-org/matrix-analytics-events.git#daad3faed54f0b1f1e026a7498b4653e4d01cd90", + "matrix-encrypt-attachment": "^1.0.3", "matrix-events-sdk": "^0.0.1-beta.7", - "matrix-js-sdk": "17.0.0", + "matrix-js-sdk": "17.1.0", "matrix-widget-api": "^0.1.0-beta.18", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", @@ -125,7 +122,6 @@ "@babel/eslint-plugin": "^7.12.10", "@babel/parser": "^7.12.11", "@babel/plugin-proposal-class-properties": "^7.12.1", - "@babel/plugin-proposal-decorators": "^7.12.12", "@babel/plugin-proposal-export-default-from": "^7.12.1", "@babel/plugin-proposal-numeric-separator": "^7.12.7", "@babel/plugin-proposal-object-rest-spread": "^7.12.1", @@ -138,7 +134,7 @@ "@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.8.tgz", "@peculiar/webcrypto": "^1.1.4", "@sentry/types": "^6.10.0", - "@sinonjs/fake-timers": "^7.0.2", + "@sinonjs/fake-timers": "^9.1.2", "@types/classnames": "^2.2.11", "@types/commonmark": "^0.27.4", "@types/counterpart": "^0.18.1", @@ -148,6 +144,7 @@ "@types/escape-html": "^1.0.1", "@types/file-saver": "^2.0.3", "@types/flux": "^3.1.9", + "@types/fs-extra": "^9.0.13", "@types/jest": "^26.0.20", "@types/lodash": "^4.14.168", "@types/modernizr": "^3.5.3", @@ -158,6 +155,7 @@ "@types/react": "17.0.14", "@types/react-beautiful-dnd": "^13.0.0", "@types/react-dom": "17.0.9", + "@types/react-test-renderer": "^17.0.1", "@types/react-transition-group": "^4.4.0", "@types/sanitize-html": "^2.3.1", "@types/zxcvbn": "^4.4.0", @@ -168,7 +166,7 @@ "babel-jest": "^26.6.3", "blob-polyfill": "^6.0.20211015", "chokidar": "^3.5.1", - "concurrently": "^5.3.0", + "cypress": "^9.5.4", "enzyme": "^3.11.0", "enzyme-to-json": "^3.6.2", "eslint": "8.9.0", @@ -178,6 +176,7 @@ "eslint-plugin-matrix-org": "^0.4.0", "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.3.0", + "fs-extra": "^10.0.1", "glob": "^7.1.6", "jest": "^27.4.0", "jest-canvas-mock": "^2.3.0", diff --git a/res/css/_common.scss b/res/css/_common.scss index 4ead7381fc..5c6349c220 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -254,7 +254,7 @@ legend { } // These are magic constants which are excluded from tinting, to let themes -// (which only have CSS, unlike skins) tell the app what their non-tinted +// (which only have CSS) tell the app what their non-tinted // colourscheme is by inspecting the stylesheet DOM. // // They are not used for layout!! diff --git a/res/css/_components.scss b/res/css/_components.scss index 3f3038ccfd..8b5dc63a0e 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -4,14 +4,21 @@ @import "./_font-sizes.scss"; @import "./_font-weights.scss"; @import "./_spacing.scss"; +@import "./components/views/beacon/_BeaconStatus.scss"; +@import "./components/views/beacon/_BeaconViewDialog.scss"; @import "./components/views/beacon/_LeftPanelLiveShareWarning.scss"; +@import "./components/views/beacon/_LiveTimeRemaining.scss"; +@import "./components/views/beacon/_OwnBeaconStatus.scss"; @import "./components/views/beacon/_RoomLiveShareWarning.scss"; @import "./components/views/beacon/_StyledLiveBeaconIcon.scss"; @import "./components/views/location/_LiveDurationDropdown.scss"; @import "./components/views/location/_LocationShareMenu.scss"; @import "./components/views/location/_MapError.scss"; +@import "./components/views/location/_Marker.scss"; @import "./components/views/location/_ShareDialogButtons.scss"; @import "./components/views/location/_ShareType.scss"; +@import "./components/views/location/_ZoomButtons.scss"; +@import "./components/views/messages/_MBeaconBody.scss"; @import "./components/views/spaces/_QuickThemeSwitcher.scss"; @import "./structures/_AutoHideScrollbar.scss"; @import "./structures/_BackdropPanel.scss"; diff --git a/res/css/components/views/beacon/_BeaconStatus.scss b/res/css/components/views/beacon/_BeaconStatus.scss new file mode 100644 index 0000000000..8ac873604d --- /dev/null +++ b/res/css/components/views/beacon/_BeaconStatus.scss @@ -0,0 +1,61 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_BeaconStatus { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + + box-sizing: border-box; + padding: $spacing-8; + + color: var(--color); + font-size: $font-12px; +} + +.mx_BeaconStatus_Loading, +.mx_BeaconStatus_Stopped { + --color: $tertiary-content; +} + +.mx_BeaconStatus_Active, +.mx_BeaconStatus_Error { + --color: $primary-content; +} + +.mx_BeaconStatus_icon { + height: 32px; + width: 32px; + + flex: 0 0 32px; + margin-right: $spacing-8; +} + +.mx_BeaconStatus_description { + flex: 1; + display: flex; + flex-direction: column; + line-height: $font-14px; + + padding-right: $spacing-8; + + // TODO handle text-overflow +} + +.mx_BeaconStatus_expiryTime { + color: $secondary-content; +} diff --git a/res/css/components/views/beacon/_BeaconViewDialog.scss b/res/css/components/views/beacon/_BeaconViewDialog.scss new file mode 100644 index 0000000000..dc4d089bfe --- /dev/null +++ b/res/css/components/views/beacon/_BeaconViewDialog.scss @@ -0,0 +1,79 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_BeaconViewDialog_wrapper .mx_Dialog { + padding: 0px; + + /* Unset contain and position to allow the close button + to appear outside the dialog */ + contain: unset; + position: unset; +} + +.mx_BeaconViewDialog { + /* subtract 0.5px to prevent single-pixel margin due to rounding */ + width: calc(80vw - 0.5px); + height: calc(80vh - 0.5px); + overflow: hidden; + + .mx_Dialog_header { + margin: 0px; + padding: 0px; + position: unset; + + .mx_Dialog_title { + display: none; + } + + .mx_Dialog_cancelButton { + z-index: 4010; + position: absolute; + right: 5vw; + top: 5vh; + width: 20px; + height: 20px; + background-color: $dialog-close-external-color; + } + } +} + +.mx_BeaconViewDialog_map { + width: 80vw; + height: 80vh; + border-radius: 8px; +} + +.mx_BeaconViewDialog_mapFallback { + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + background: url('$(res)/img/location/map.svg'); + background-size: cover; +} + +.mx_BeaconViewDialog_mapFallbackIcon { + width: 65px; + margin-bottom: $spacing-16; + color: $quaternary-content; +} + +.mx_BeaconViewDialog_mapFallbackMessage { + color: $secondary-content; + margin-bottom: $spacing-16; +} diff --git a/res/css/components/views/beacon/_LiveTimeRemaining.scss b/res/css/components/views/beacon/_LiveTimeRemaining.scss new file mode 100644 index 0000000000..de13f7aab2 --- /dev/null +++ b/res/css/components/views/beacon/_LiveTimeRemaining.scss @@ -0,0 +1,20 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_LiveTimeRemaining { + color: $secondary-content; + font-size: $font-12px; +} diff --git a/res/css/components/views/beacon/_OwnBeaconStatus.scss b/res/css/components/views/beacon/_OwnBeaconStatus.scss new file mode 100644 index 0000000000..aa01b6269a --- /dev/null +++ b/res/css/components/views/beacon/_OwnBeaconStatus.scss @@ -0,0 +1,27 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_EventTile[data-layout="bubble"] .mx_OwnBeaconStatus_button { + // align to top to make room for timestamp + // in bubble view + align-self: start; +} + +.mx_OwnBeaconStatus_destructiveButton { + // override button link_inline styles + color: $alert !important; + font-weight: $font-semi-bold !important; +} diff --git a/res/css/components/views/beacon/_RoomLiveShareWarning.scss b/res/css/components/views/beacon/_RoomLiveShareWarning.scss index 7404f88aea..f82c7f4de4 100644 --- a/res/css/components/views/beacon/_RoomLiveShareWarning.scss +++ b/res/css/components/views/beacon/_RoomLiveShareWarning.scss @@ -39,12 +39,6 @@ limitations under the License. font-size: $font-15px; } -.mx_RoomLiveShareWarning_expiry { - color: $secondary-content; - font-size: $font-12px; - margin-right: $spacing-16; -} - .mx_RoomLiveShareWarning_spinner { margin-right: $spacing-16; } @@ -54,6 +48,10 @@ limitations under the License. margin-left: $spacing-16; } +.mx_RoomLiveShareWarning_stopButton { + margin-left: $spacing-16; +} + .mx_RoomLiveShareWarning_closeButtonIcon { height: $font-18px; padding: $spacing-4; diff --git a/res/css/components/views/beacon/_StyledLiveBeaconIcon.scss b/res/css/components/views/beacon/_StyledLiveBeaconIcon.scss index e31279c34b..9096c3c71f 100644 --- a/res/css/components/views/beacon/_StyledLiveBeaconIcon.scss +++ b/res/css/components/views/beacon/_StyledLiveBeaconIcon.scss @@ -33,3 +33,8 @@ limitations under the License. background-color: $alert; border-color: $alert; } + +.mx_StyledLiveBeaconIcon.mx_StyledLiveBeaconIcon_idle { + background-color: $quaternary-content; + border-color: $quaternary-content; +} diff --git a/res/css/components/views/location/_Marker.scss b/res/css/components/views/location/_Marker.scss new file mode 100644 index 0000000000..7a1baccf9f --- /dev/null +++ b/res/css/components/views/location/_Marker.scss @@ -0,0 +1,46 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_Marker_defaultColor { + color: $accent; +} + +.mx_Marker_border { + width: 42px; + height: 42px; + border-radius: 50%; + filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.2)); + background-color: currentColor; + + display: flex; + justify-content: center; + align-items: center; + + // caret down + &::before { + content: ''; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-top: 5px solid currentColor; + position: absolute; + bottom: -4px; + } +} + +.mx_Marker_icon { + color: white; + height: 20px; +} diff --git a/res/css/components/views/location/_ZoomButtons.scss b/res/css/components/views/location/_ZoomButtons.scss new file mode 100644 index 0000000000..59d52477f9 --- /dev/null +++ b/res/css/components/views/location/_ZoomButtons.scss @@ -0,0 +1,45 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_ZoomButtons { + position: absolute; + bottom: $spacing-32; + right: $spacing-24; +} + +.mx_ZoomButtons_button { + @mixin ButtonResetDefault; + + margin-top: $spacing-8; + border-radius: 4px; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + + height: 24px; + width: 24px; + + background: $background; + box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25); +} + +.mx_ZoomButtons_icon { + height: 10px; + width: 10px; + + color: $primary-content; +} diff --git a/res/css/components/views/messages/_MBeaconBody.scss b/res/css/components/views/messages/_MBeaconBody.scss new file mode 100644 index 0000000000..dc63d6676d --- /dev/null +++ b/res/css/components/views/messages/_MBeaconBody.scss @@ -0,0 +1,65 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_MBeaconBody { + position: relative; + height: 220px; + width: 325px; + + border-radius: $timeline-image-border-radius; + overflow: hidden; +} + +.mx_MBeaconBody_map { + height: 100%; + width: 100%; + z-index: 0; // keeps the entire map under the message action bar + + &:not(.mx_MBeaconBody_mapFallback) { + cursor: pointer; + } +} + +.mx_MBeaconBody_mapFallback { + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + + // pushes spinner/icon up + // to appear more centered with the footer + padding-bottom: 50px; + + background: url('$(res)/img/location/map.svg'); + background-size: cover; +} + +.mx_MBeaconBody_mapFallbackIcon { + width: 65px; + color: $quaternary-content; +} + +.mx_MBeaconBody_chin { + position: absolute; + bottom: 0; + width: 100%; + background-color: $overlay-background; +} + +.mx_EventTile[data-layout="bubble"] .mx_EventTile_line .mx_MBeaconBody { + max-width: 100%; + width: 450px; +} diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index 74d30bf59a..1cbc7cd735 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -251,7 +251,8 @@ $activeBorderColor: $primary-content; margin-top: auto; margin-bottom: auto; display: none; - position: relative; + position: absolute; + right: 4px; &::before { top: 3px; @@ -327,6 +328,16 @@ $activeBorderColor: $primary-content; } } + .mx_SpaceItem:not(.mx_SpaceItem_new) { + .mx_SpaceButton:hover, + .mx_SpaceButton:focus-within, + .mx_SpaceButton_hasMenuOpen { + &:not(.mx_SpaceButton_narrow):not(.mx_SpaceButton_invite) .mx_SpaceButton_name { + max-width: calc(100% - 56px); + } + } + } + /* root space buttons are bigger and not indented */ & > .mx_AutoHideScrollbar { flex: 1; diff --git a/res/css/structures/_ToastContainer.scss b/res/css/structures/_ToastContainer.scss index 5137aed95a..00c7cd05b4 100644 --- a/res/css/structures/_ToastContainer.scss +++ b/res/css/structures/_ToastContainer.scss @@ -108,26 +108,25 @@ limitations under the License. } .mx_Toast_title { + display: flex; + align-items: center; + column-gap: 8px; width: 100%; box-sizing: border-box; h2 { - grid-column: 1 / 3; - grid-row: 1; margin: 0; font-size: $font-15px; font-weight: 600; display: inline; width: auto; - vertical-align: middle; } - span { - padding-left: 8px; - float: right; + .mx_Toast_title_countIndicator { font-size: $font-12px; line-height: $font-22px; color: $secondary-content; + margin-inline-start: auto; // on the end side of the div } } @@ -137,17 +136,14 @@ limitations under the License. } .mx_Toast_buttons { - float: right; display: flex; + justify-content: flex-end; + column-gap: 5px; .mx_AccessibleButton { min-width: 96px; box-sizing: border-box; } - - .mx_AccessibleButton + .mx_AccessibleButton { - margin-left: 5px; - } } .mx_Toast_description { @@ -157,10 +153,6 @@ limitations under the License. margin: 4px 0 11px 0; font-size: $font-12px; - .mx_AccessibleButton_kind_link { - font-size: inherit; - } - a { text-decoration: none; } diff --git a/res/css/structures/_UserMenu.scss b/res/css/structures/_UserMenu.scss index 90b825c886..a88e9eddb9 100644 --- a/res/css/structures/_UserMenu.scss +++ b/res/css/structures/_UserMenu.scss @@ -147,11 +147,6 @@ limitations under the License. margin-top: 8px; } } - - .mx_AccessibleButton_kind_link { - font-weight: normal; - font-size: inherit; - } } } diff --git a/res/css/structures/_ViewSource.scss b/res/css/structures/_ViewSource.scss index e3d6135ef3..f1ada65786 100644 --- a/res/css/structures/_ViewSource.scss +++ b/res/css/structures/_ViewSource.scss @@ -34,8 +34,13 @@ limitations under the License. padding: 0.5em 1em 0.5em 1em; word-wrap: break-word; white-space: pre-wrap; + overflow-wrap: anywhere; } .mx_ViewSource_details { margin-top: 0.8em; } + +.mx_ViewSource_container { + max-width: calc(100% - 24px); +} diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index 0e6912e435..638917f1c2 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -88,13 +88,12 @@ limitations under the License. div.mx_AccessibleButton_kind_link.mx_Login_forgot { display: block; margin: 0 auto; - // style it as a link - font-size: inherit; &.mx_AccessibleButton_disabled { cursor: not-allowed; } } + .mx_Login_spinner { display: flex; justify-content: center; diff --git a/res/css/views/context_menus/_MessageContextMenu.scss b/res/css/views/context_menus/_MessageContextMenu.scss index e743619f8f..b92ce10d35 100644 --- a/res/css/views/context_menus/_MessageContextMenu.scss +++ b/res/css/views/context_menus/_MessageContextMenu.scss @@ -90,6 +90,22 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/room/pin.svg'); } + .mx_MessageContextMenu_iconCopy::before { + mask-image: url($copy-button-url); + } + + .mx_MessageContextMenu_iconEdit::before { + mask-image: url('$(res)/img/element-icons/room/message-bar/edit.svg'); + } + + .mx_MessageContextMenu_iconReply::before { + mask-image: url('$(res)/img/element-icons/room/message-bar/reply.svg'); + } + + .mx_MessageContextMenu_iconReact::before { + mask-image: url('$(res)/img/element-icons/room/message-bar/emoji.svg'); + } + .mx_MessageContextMenu_iconViewInRoom::before { mask-image: url('$(res)/img/element-icons/view-in-room.svg'); } diff --git a/res/css/views/dialogs/_FeedbackDialog.scss b/res/css/views/dialogs/_FeedbackDialog.scss index 588f65eb65..8d11b83fe1 100644 --- a/res/css/views/dialogs/_FeedbackDialog.scss +++ b/res/css/views/dialogs/_FeedbackDialog.scss @@ -58,10 +58,6 @@ limitations under the License. line-height: $font-15px; } - .mx_AccessibleButton_kind_link { - font-size: inherit; - } - a, .mx_AccessibleButton_kind_link { color: $accent; text-decoration: underline; diff --git a/res/css/views/dialogs/_GenericFeatureFeedbackDialog.scss b/res/css/views/dialogs/_GenericFeatureFeedbackDialog.scss index 3898e09756..83f9349514 100644 --- a/res/css/views/dialogs/_GenericFeatureFeedbackDialog.scss +++ b/res/css/views/dialogs/_GenericFeatureFeedbackDialog.scss @@ -21,9 +21,4 @@ limitations under the License. line-height: $font-20px; margin-bottom: 24px; } - - .mx_AccessibleButton_kind_link { - font-size: inherit; - line-height: inherit; - } } diff --git a/res/css/views/dialogs/_LocationViewDialog.scss b/res/css/views/dialogs/_LocationViewDialog.scss index e7cdaf8800..600c308265 100644 --- a/res/css/views/dialogs/_LocationViewDialog.scss +++ b/res/css/views/dialogs/_LocationViewDialog.scss @@ -48,49 +48,10 @@ limitations under the License. background-color: $dialog-close-external-color; } } - - .mx_MLocationBody { - position: absolute; - - .mx_MLocationBody_map { - width: 80vw; - height: 80vh; - } - - .mx_MLocationBody_zoomButtons { - position: absolute; - display: grid; - grid-template-columns: auto; - grid-row-gap: 8px; - - right: 24px; - bottom: 48px; - - .mx_AccessibleButton { - background-color: $background; - box-shadow: 0px 4px 12px rgba(0, 0, 0, 0.25); - border-radius: 4px; - width: 24px; - height: 24px; - - .mx_MLocationBody_zoomButton { - background-color: $primary-content; - margin: 4px; - width: 16px; - height: 16px; - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; - } - - .mx_MLocationBody_plusButton { - mask-image: url('$(res)/img/element-icons/plus-button.svg'); - } - - .mx_MLocationBody_minusButton { - mask-image: url('$(res)/img/element-icons/minus-button.svg'); - } - } - } - } +} + +.mx_LocationViewDialog_map { + width: 80vw; + height: 80vh; + border-radius: 8px; } diff --git a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss index f18b4917cf..07735ad027 100644 --- a/res/css/views/dialogs/_RoomSettingsDialogBridges.scss +++ b/res/css/views/dialogs/_RoomSettingsDialogBridges.scss @@ -22,84 +22,99 @@ limitations under the License. margin: 0; padding: 0; } -} -.mx_RoomSettingsDialog_BridgeList li { - list-style-type: none; - padding: 5px; - margin-bottom: 8px; - border-width: 1px 1px; - border-color: $primary-hairline-color; - border-style: solid; - border-radius: 5px; + li { + list-style-type: none; - .column-icon { - float: left; - padding-right: 10px; + &.mx_RoomSettingsDialog_BridgeList_listItem { + display: flex; + flex-wrap: wrap; + gap: $spacing-8; + padding: 5px; + margin-bottom: $spacing-8; - * { + // border-style around each bridge list item + border-width: 1px 1px; + border-color: $primary-hairline-color; + border-style: solid; border-radius: 5px; - border: 1px solid $input-darker-bg-color; - } - .noProtocolIcon { - width: 48px; - height: 48px; - background: $input-darker-bg-color; - border-radius: 5px; - } + .mx_RoomSettingsDialog_column_icon { + .mx_RoomSettingsDialog_protocolIcon, + .mx_RoomSettingsDialog_protocolIcon span, + .mx_RoomSettingsDialog_noProtocolIcon { + box-sizing: border-box; + border-radius: 5px; + border: 1px solid $input-darker-bg-color; + } - .protocol-icon { - float: left; - margin-right: 5px; - img { - border-radius: 5px; - border-width: 1px 1px; - border-color: $primary-hairline-color; + .mx_RoomSettingsDialog_noProtocolIcon, + .mx_RoomSettingsDialog_protocolIcon img { + border-radius: 5px; + } + + .mx_RoomSettingsDialog_noProtocolIcon { + width: 48px; + height: 48px; + background: $input-darker-bg-color; + } + + .mx_RoomSettingsDialog_protocolIcon { + img { + border-width: 1px 1px; + border-color: $primary-hairline-color; + } + + span { + /* Correct letter placement */ + left: auto; + } + } } - span { - /* Correct letter placement */ - left: auto; - } - } - } - .column-data { - display: inline-block; - width: 85%; + .mx_RoomSettingsDialog_column_data { + display: inline-block; + width: 85%; - > h3 { - margin-top: 0px; - margin-bottom: 0px; - font-size: 16pt; - color: $primary-content; - } + .mx_RoomSettingsDialog_column_data_details, + .mx_RoomSettingsDialog_column_data_metadata, + .mx_RoomSettingsDialog_column_data_metadata li, + .mx_RoomSettingsDialog_column_data_protocolName { + margin-bottom: 0; + } - > * { - margin-top: 4px; - margin-bottom: 0; - } + .mx_RoomSettingsDialog_column_data_details, + .mx_RoomSettingsDialog_column_data_metadata { + margin-top: $spacing-4; + } - .workspace-channel-details { - color: $primary-content; - font-weight: 600; + .mx_RoomSettingsDialog_column_data_metadata li { + margin-top: $spacing-8; + } - .channel { - margin-left: 5px; - } - } + .mx_RoomSettingsDialog_column_data_protocolName { + margin-top: 0; + font-size: 16pt; + color: $primary-content; + } - .metadata { - color: $muted-fg-color; - margin-bottom: 0; - overflow-y: visible; - text-overflow: ellipsis; - white-space: normal; - padding: 0; + .mx_RoomSettingsDialog_workspace_channel_details { + color: $primary-content; + font-weight: $font-semi-bold; - > li { - padding: 0; - border: 0; + .mx_RoomSettingsDialog_channel { + margin-inline-start: 5px; + } + } + + .mx_RoomSettingsDialog_metadata { + color: $muted-fg-color; + margin-bottom: 0; + overflow-y: visible; + text-overflow: ellipsis; + white-space: normal; + padding: 0; + } } } } diff --git a/res/css/views/dialogs/_SpaceSettingsDialog.scss b/res/css/views/dialogs/_SpaceSettingsDialog.scss index 31a544d166..ead082dba6 100644 --- a/res/css/views/dialogs/_SpaceSettingsDialog.scss +++ b/res/css/views/dialogs/_SpaceSettingsDialog.scss @@ -77,6 +77,7 @@ limitations under the License. .mx_AccessibleButton_hasKind { &.mx_AccessibleButton_kind_link { + font-size: $font-14px; margin: 7px 18px; &.mx_SettingsTab_showAdvanced { diff --git a/res/css/views/dialogs/_SpotlightDialog.scss b/res/css/views/dialogs/_SpotlightDialog.scss index 3041c86042..9f78d905b5 100644 --- a/res/css/views/dialogs/_SpotlightDialog.scss +++ b/res/css/views/dialogs/_SpotlightDialog.scss @@ -241,7 +241,6 @@ limitations under the License. .mx_SpotlightDialog_recentSearches > h4 > .mx_AccessibleButton_kind_link { padding: 0; float: right; - font-weight: normal; font-size: $font-12px; line-height: $font-15px; color: $secondary-content; diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index a87aab7af8..021e8ffc8c 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -39,6 +39,7 @@ limitations under the License. justify-content: center; font-size: $font-14px; border: none; // override default "`; +exports[` when user has live beacons and geolocation is available renders correctly with one live beacon in room 1`] = `"

You are sharing your live location1h left
"`; -exports[` when user has live beacons and geolocation is available renders correctly with two live beacons in room 1`] = `"
You are sharing your live location12h left
"`; +exports[` when user has live beacons and geolocation is available renders correctly with two live beacons in room 1`] = `"
You are sharing your live location12h left
"`; -exports[` when user has live beacons and geolocation is available stopping beacons displays error when stop sharing fails 1`] = `"
An error occurred while stopping your live location, please try again
"`; +exports[` when user has live beacons and geolocation is available stopping beacons displays error when stop sharing fails 1`] = `"
An error occurred while stopping your live location, please try again
"`; exports[` when user has live beacons and geolocation is available with wire errors displays wire error when mounted with wire errors 1`] = ` when user has live beacons and geolocation is when user has live beacons and geolocation is An error occured whilst sharing your live location, please try again when user has live beacons and geolocation is tabIndex={0} >