Merge remote-tracking branch 'origin/release-v3.43.0'
# Conflicts: # CHANGELOG.md # package.json # src/components/views/messages/MessageActionBar.tsx
|
@ -1,5 +1,6 @@
|
||||||
src/component-index.js
|
|
||||||
test/end-to-end-tests/node_modules/
|
test/end-to-end-tests/node_modules/
|
||||||
test/end-to-end-tests/element/
|
test/end-to-end-tests/element/
|
||||||
test/end-to-end-tests/synapse/
|
test/end-to-end-tests/synapse/
|
||||||
test/end-to-end-tests/lib/
|
test/end-to-end-tests/lib/
|
||||||
|
# Legacy skinning file that some people might still have
|
||||||
|
src/component-index.js
|
||||||
|
|
|
@ -92,6 +92,7 @@ module.exports = {
|
||||||
files: [
|
files: [
|
||||||
"src/**/*.{ts,tsx}",
|
"src/**/*.{ts,tsx}",
|
||||||
"test/**/*.{ts,tsx}",
|
"test/**/*.{ts,tsx}",
|
||||||
|
"cypress/**/*.ts",
|
||||||
],
|
],
|
||||||
extends: [
|
extends: [
|
||||||
"plugin:matrix-org/typescript",
|
"plugin:matrix-org/typescript",
|
||||||
|
|
49
.github/workflows/element-build-and-test.yaml
vendored
Normal file
|
@ -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
|
23
.github/workflows/layered-build.yaml
vendored
|
@ -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
|
|
||||||
|
|
2
.github/workflows/netlify.yaml
vendored
|
@ -3,7 +3,7 @@
|
||||||
name: Upload Preview Build to Netlify
|
name: Upload Preview Build to Netlify
|
||||||
on:
|
on:
|
||||||
workflow_run:
|
workflow_run:
|
||||||
workflows: ["Layered Preview Build"]
|
workflows: ["Element Web - Build and Test"]
|
||||||
types:
|
types:
|
||||||
- completed
|
- completed
|
||||||
jobs:
|
jobs:
|
||||||
|
|
3
.github/workflows/test_coverage.yml
vendored
|
@ -25,10 +25,11 @@ jobs:
|
||||||
run: "./scripts/ci/install-deps.sh --ignore-scripts"
|
run: "./scripts/ci/install-deps.sh --ignore-scripts"
|
||||||
|
|
||||||
- name: Run tests with coverage
|
- name: Run tests with coverage
|
||||||
run: "yarn install && yarn reskindex && yarn coverage"
|
run: "yarn install && yarn coverage"
|
||||||
|
|
||||||
- name: Upload coverage
|
- name: Upload coverage
|
||||||
uses: codecov/codecov-action@v2
|
uses: codecov/codecov-action@v2
|
||||||
with:
|
with:
|
||||||
fail_ci_if_error: false
|
fail_ci_if_error: false
|
||||||
verbose: true
|
verbose: true
|
||||||
|
override_commit: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || '' }}
|
||||||
|
|
9
.gitignore
vendored
|
@ -11,6 +11,7 @@ package-lock.json
|
||||||
/matrix-react-sdk-*.tgz
|
/matrix-react-sdk-*.tgz
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
|
# Legacy skinning file that some people might still have
|
||||||
/src/component-index.js
|
/src/component-index.js
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
@ -18,3 +19,11 @@ package-lock.json
|
||||||
|
|
||||||
.vscode
|
.vscode
|
||||||
.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
|
||||||
|
|
45
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)
|
Changes in [3.42.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.42.4) (2022-04-14)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
|
|
||||||
|
|
|
@ -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
|
component is a view or a structure, and then a broad functional grouping
|
||||||
(e.g. 'rooms' here)
|
(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)
|
|
||||||
<!-- TODO: Remove this once this approach to skinning is replaced -->
|
|
||||||
|
|
||||||
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
* The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css).
|
||||||
CSS for matrix-react-sdk currently resides in
|
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
|
* 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
|
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
|
git checkout develop
|
||||||
yarn link matrix-js-sdk
|
yarn link matrix-js-sdk
|
||||||
yarn install
|
yarn install
|
||||||
|
|
||||||
# Generate the `component-index.js` file.
|
|
||||||
yarn reskindex
|
|
||||||
```
|
```
|
||||||
|
|
||||||
See the [help for `yarn link`](https://classic.yarnpkg.com/docs/cli/link) for
|
See the [help for `yarn link`](https://classic.yarnpkg.com/docs/cli/link) for
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
const EventEmitter = require("events");
|
const EventEmitter = require("events");
|
||||||
const { LngLat, NavigationControl } = require('maplibre-gl');
|
const { LngLat, NavigationControl, LngLatBounds } = require('maplibre-gl');
|
||||||
|
|
||||||
class MockMap extends EventEmitter {
|
class MockMap extends EventEmitter {
|
||||||
addControl = jest.fn();
|
addControl = jest.fn();
|
||||||
removeControl = jest.fn();
|
removeControl = jest.fn();
|
||||||
|
zoomIn = jest.fn();
|
||||||
|
zoomOut = jest.fn();
|
||||||
|
setCenter = jest.fn();
|
||||||
|
setStyle = jest.fn();
|
||||||
|
fitBounds = jest.fn();
|
||||||
}
|
}
|
||||||
const MockMapInstance = new MockMap();
|
const MockMapInstance = new MockMap();
|
||||||
|
|
||||||
|
@ -14,10 +19,12 @@ const MockGeolocateInstance = new MockGeolocateControl();
|
||||||
const MockMarker = {}
|
const MockMarker = {}
|
||||||
MockMarker.setLngLat = jest.fn().mockReturnValue(MockMarker);
|
MockMarker.setLngLat = jest.fn().mockReturnValue(MockMarker);
|
||||||
MockMarker.addTo = jest.fn().mockReturnValue(MockMarker);
|
MockMarker.addTo = jest.fn().mockReturnValue(MockMarker);
|
||||||
|
MockMarker.remove = jest.fn().mockReturnValue(MockMarker);
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Map: jest.fn().mockReturnValue(MockMapInstance),
|
Map: jest.fn().mockReturnValue(MockMapInstance),
|
||||||
GeolocateControl: jest.fn().mockReturnValue(MockGeolocateInstance),
|
GeolocateControl: jest.fn().mockReturnValue(MockGeolocateInstance),
|
||||||
Marker: jest.fn().mockReturnValue(MockMarker),
|
Marker: jest.fn().mockReturnValue(MockMarker),
|
||||||
LngLat,
|
LngLat,
|
||||||
NavigationControl
|
LngLatBounds,
|
||||||
|
NavigationControl,
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,7 +13,6 @@ module.exports = {
|
||||||
"@babel/preset-react",
|
"@babel/preset-react",
|
||||||
],
|
],
|
||||||
"plugins": [
|
"plugins": [
|
||||||
["@babel/plugin-proposal-decorators", {legacy: true}],
|
|
||||||
"@babel/plugin-proposal-export-default-from",
|
"@babel/plugin-proposal-export-default-from",
|
||||||
"@babel/plugin-proposal-numeric-separator",
|
"@babel/plugin-proposal-numeric-separator",
|
||||||
"@babel/plugin-proposal-class-properties",
|
"@babel/plugin-proposal-class-properties",
|
||||||
|
|
4
cypress.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"baseUrl": "http://localhost:8080",
|
||||||
|
"videoUploadOnPasses": false
|
||||||
|
}
|
52
cypress/integration/1-register/register.spec.ts
Normal file
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { SynapseInstance } from "../../plugins/synapsedocker/index";
|
||||||
|
|
||||||
|
describe("Registration", () => {
|
||||||
|
let synapseId;
|
||||||
|
let synapsePort;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.task<SynapseInstance>("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');
|
||||||
|
});
|
||||||
|
});
|
|
@ -14,16 +14,10 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/// <reference types="cypress" />
|
||||||
* 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 = {};
|
export default function(on, config) {
|
||||||
|
synapseDocker(on, config);
|
||||||
sdk.loadSkin({ components });
|
}
|
||||||
|
|
||||||
export default sdk;
|
|
212
cypress/plugins/synapsedocker/index.ts
Normal file
|
@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
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<string, SynapseInstance>();
|
||||||
|
|
||||||
|
function randB64Bytes(numBytes: number): string {
|
||||||
|
return crypto.randomBytes(numBytes).toString("base64").replace(/=*$/, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cfgDirFromTemplate(template: string): Promise<SynapseConfig> {
|
||||||
|
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<SynapseInstance> {
|
||||||
|
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<string>((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<number>((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<void>((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<void>((resolve, reject) => {
|
||||||
|
childProcess.execFile('docker', [
|
||||||
|
"stop",
|
||||||
|
id,
|
||||||
|
], err => {
|
||||||
|
if (err) reject(err);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
await new Promise<void>((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"));
|
||||||
|
});
|
||||||
|
}
|
3
cypress/plugins/synapsedocker/templates/COPYME/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Meta-template for synapse templates
|
||||||
|
|
||||||
|
To make another template, you can copy this directory
|
|
@ -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 <noreply@example.com>"
|
||||||
|
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
|
50
cypress/plugins/synapsedocker/templates/COPYME/log.config
Normal file
|
@ -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
|
|
@ -0,0 +1 @@
|
||||||
|
A synapse configured with user privacy consent enabled
|
|
@ -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 <noreply@example.com>"
|
||||||
|
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
|
50
cypress/plugins/synapsedocker/templates/consent/log.config
Normal file
|
@ -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
|
|
@ -0,0 +1,23 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Test Privacy policy</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% if has_consented %}
|
||||||
|
<p>
|
||||||
|
Thank you, you've already accepted the license.
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
Please accept the license!
|
||||||
|
</p>
|
||||||
|
<form method="post" action="consent">
|
||||||
|
<input type="hidden" name="v" value="{{version}}"/>
|
||||||
|
<input type="hidden" name="u" value="{{user}}"/>
|
||||||
|
<input type="hidden" name="h" value="{{userhmac}}"/>
|
||||||
|
<input type="submit" value="Sure thing!"/>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>Test Privacy policy</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>Danke schon</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
3
cypress/support/index.ts
Normal file
|
@ -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).
|
9
cypress/tsconfig.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es2016",
|
||||||
|
"lib": ["es2020", "dom"],
|
||||||
|
"types": ["cypress"],
|
||||||
|
"moduleResolution": "node"
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"]
|
||||||
|
}
|
|
@ -1,71 +1,18 @@
|
||||||
# Skinning
|
# Skinning
|
||||||
|
|
||||||
The react-sdk can be skinned to replace presentation components, CSS, or
|
Skinning in the context of the react-sdk is component replacement rather than CSS. This means you can override (replace)
|
||||||
other relevant parts of the SDK. Typically consumers will replace entire
|
any accessible component in the project to implement custom behaviour, look & feel, etc. Depending on your approach,
|
||||||
components and get the ability for custom CSS as a result.
|
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
|
At present, the react-sdk offers no stable interface for components - this means properties and state can and do change
|
||||||
some of the more complicated parts such as component replacement.
|
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
|
Taking a look at [element-web](https://github.com/vector-im/element-web)'s approach to skinning may be worthwhile, as it
|
||||||
exposes). This can typically be done with a npm script like `"reskindex -h src/header"`.
|
overrides some relatively simple components.
|
||||||
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.
|
|
||||||
|
|
|
@ -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 <p/> 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.
|
|
31
package.json
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.42.4",
|
"version": "3.43.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -22,9 +22,6 @@
|
||||||
"README.md",
|
"README.md",
|
||||||
"package.json"
|
"package.json"
|
||||||
],
|
],
|
||||||
"bin": {
|
|
||||||
"reskindex": "scripts/reskindex.js"
|
|
||||||
},
|
|
||||||
"main": "./lib/index.ts",
|
"main": "./lib/index.ts",
|
||||||
"matrix_src_main": "./src/index.ts",
|
"matrix_src_main": "./src/index.ts",
|
||||||
"matrix_lib_main": "./lib/index.ts",
|
"matrix_lib_main": "./lib/index.ts",
|
||||||
|
@ -37,23 +34,23 @@
|
||||||
"i18n": "matrix-gen-i18n",
|
"i18n": "matrix-gen-i18n",
|
||||||
"prunei18n": "matrix-prune-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",
|
"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",
|
"make-component": "node scripts/make-react-component.js",
|
||||||
"reskindex:watch": "node scripts/reskindex.js -h header -w",
|
|
||||||
"rethemendex": "res/css/rethemendex.sh",
|
"rethemendex": "res/css/rethemendex.sh",
|
||||||
"clean": "rimraf lib",
|
"clean": "rimraf lib",
|
||||||
"build": "yarn clean && git rev-parse HEAD > git-revision.txt && yarn build:compile && yarn build:types",
|
"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",
|
"build:types": "tsc --emitDeclarationOnly --jsx react",
|
||||||
"start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && yarn start:all",
|
"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\"",
|
"start:build": "babel src -w -s -d lib --verbose --extensions \".ts,.js\"",
|
||||||
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
"lint": "yarn lint:types && yarn lint:js && yarn lint:style",
|
||||||
"lint:js": "eslint --max-warnings 0 src test",
|
"lint:js": "eslint --max-warnings 0 src test cypress",
|
||||||
"lint:js-fix": "eslint --fix src test",
|
"lint:js-fix": "eslint --fix src test cypress",
|
||||||
"lint:types": "tsc --noEmit --jsx react",
|
"lint:types": "tsc --noEmit --jsx react && tsc --noEmit -p cypress",
|
||||||
"lint:style": "stylelint \"res/css/**/*.scss\"",
|
"lint:style": "stylelint \"res/css/**/*.scss\"",
|
||||||
"test": "jest",
|
"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",
|
"test:e2e": "./test/end-to-end-tests/run.sh --app-url http://localhost:8080",
|
||||||
"coverage": "yarn test --coverage"
|
"coverage": "yarn test --coverage"
|
||||||
},
|
},
|
||||||
|
@ -64,7 +61,6 @@
|
||||||
"@types/geojson": "^7946.0.8",
|
"@types/geojson": "^7946.0.8",
|
||||||
"await-lock": "^2.1.0",
|
"await-lock": "^2.1.0",
|
||||||
"blurhash": "^1.1.3",
|
"blurhash": "^1.1.3",
|
||||||
"browser-encrypt-attachment": "^0.3.0",
|
|
||||||
"browser-request": "^0.3.3",
|
"browser-request": "^0.3.3",
|
||||||
"cheerio": "^1.0.0-rc.9",
|
"cheerio": "^1.0.0-rc.9",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
|
@ -93,8 +89,9 @@
|
||||||
"lodash": "^4.17.20",
|
"lodash": "^4.17.20",
|
||||||
"maplibre-gl": "^1.15.2",
|
"maplibre-gl": "^1.15.2",
|
||||||
"matrix-analytics-events": "github:matrix-org/matrix-analytics-events.git#daad3faed54f0b1f1e026a7498b4653e4d01cd90",
|
"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-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",
|
"matrix-widget-api": "^0.1.0-beta.18",
|
||||||
"minimist": "^1.2.5",
|
"minimist": "^1.2.5",
|
||||||
"opus-recorder": "^8.0.3",
|
"opus-recorder": "^8.0.3",
|
||||||
|
@ -125,7 +122,6 @@
|
||||||
"@babel/eslint-plugin": "^7.12.10",
|
"@babel/eslint-plugin": "^7.12.10",
|
||||||
"@babel/parser": "^7.12.11",
|
"@babel/parser": "^7.12.11",
|
||||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
"@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-export-default-from": "^7.12.1",
|
||||||
"@babel/plugin-proposal-numeric-separator": "^7.12.7",
|
"@babel/plugin-proposal-numeric-separator": "^7.12.7",
|
||||||
"@babel/plugin-proposal-object-rest-spread": "^7.12.1",
|
"@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",
|
"@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",
|
"@peculiar/webcrypto": "^1.1.4",
|
||||||
"@sentry/types": "^6.10.0",
|
"@sentry/types": "^6.10.0",
|
||||||
"@sinonjs/fake-timers": "^7.0.2",
|
"@sinonjs/fake-timers": "^9.1.2",
|
||||||
"@types/classnames": "^2.2.11",
|
"@types/classnames": "^2.2.11",
|
||||||
"@types/commonmark": "^0.27.4",
|
"@types/commonmark": "^0.27.4",
|
||||||
"@types/counterpart": "^0.18.1",
|
"@types/counterpart": "^0.18.1",
|
||||||
|
@ -148,6 +144,7 @@
|
||||||
"@types/escape-html": "^1.0.1",
|
"@types/escape-html": "^1.0.1",
|
||||||
"@types/file-saver": "^2.0.3",
|
"@types/file-saver": "^2.0.3",
|
||||||
"@types/flux": "^3.1.9",
|
"@types/flux": "^3.1.9",
|
||||||
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/jest": "^26.0.20",
|
"@types/jest": "^26.0.20",
|
||||||
"@types/lodash": "^4.14.168",
|
"@types/lodash": "^4.14.168",
|
||||||
"@types/modernizr": "^3.5.3",
|
"@types/modernizr": "^3.5.3",
|
||||||
|
@ -158,6 +155,7 @@
|
||||||
"@types/react": "17.0.14",
|
"@types/react": "17.0.14",
|
||||||
"@types/react-beautiful-dnd": "^13.0.0",
|
"@types/react-beautiful-dnd": "^13.0.0",
|
||||||
"@types/react-dom": "17.0.9",
|
"@types/react-dom": "17.0.9",
|
||||||
|
"@types/react-test-renderer": "^17.0.1",
|
||||||
"@types/react-transition-group": "^4.4.0",
|
"@types/react-transition-group": "^4.4.0",
|
||||||
"@types/sanitize-html": "^2.3.1",
|
"@types/sanitize-html": "^2.3.1",
|
||||||
"@types/zxcvbn": "^4.4.0",
|
"@types/zxcvbn": "^4.4.0",
|
||||||
|
@ -168,7 +166,7 @@
|
||||||
"babel-jest": "^26.6.3",
|
"babel-jest": "^26.6.3",
|
||||||
"blob-polyfill": "^6.0.20211015",
|
"blob-polyfill": "^6.0.20211015",
|
||||||
"chokidar": "^3.5.1",
|
"chokidar": "^3.5.1",
|
||||||
"concurrently": "^5.3.0",
|
"cypress": "^9.5.4",
|
||||||
"enzyme": "^3.11.0",
|
"enzyme": "^3.11.0",
|
||||||
"enzyme-to-json": "^3.6.2",
|
"enzyme-to-json": "^3.6.2",
|
||||||
"eslint": "8.9.0",
|
"eslint": "8.9.0",
|
||||||
|
@ -178,6 +176,7 @@
|
||||||
"eslint-plugin-matrix-org": "^0.4.0",
|
"eslint-plugin-matrix-org": "^0.4.0",
|
||||||
"eslint-plugin-react": "^7.28.0",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.3.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
|
"fs-extra": "^10.0.1",
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"jest": "^27.4.0",
|
"jest": "^27.4.0",
|
||||||
"jest-canvas-mock": "^2.3.0",
|
"jest-canvas-mock": "^2.3.0",
|
||||||
|
|
|
@ -254,7 +254,7 @@ legend {
|
||||||
}
|
}
|
||||||
|
|
||||||
// These are magic constants which are excluded from tinting, to let themes
|
// 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.
|
// colourscheme is by inspecting the stylesheet DOM.
|
||||||
//
|
//
|
||||||
// They are not used for layout!!
|
// They are not used for layout!!
|
||||||
|
|
|
@ -4,14 +4,21 @@
|
||||||
@import "./_font-sizes.scss";
|
@import "./_font-sizes.scss";
|
||||||
@import "./_font-weights.scss";
|
@import "./_font-weights.scss";
|
||||||
@import "./_spacing.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/_LeftPanelLiveShareWarning.scss";
|
||||||
|
@import "./components/views/beacon/_LiveTimeRemaining.scss";
|
||||||
|
@import "./components/views/beacon/_OwnBeaconStatus.scss";
|
||||||
@import "./components/views/beacon/_RoomLiveShareWarning.scss";
|
@import "./components/views/beacon/_RoomLiveShareWarning.scss";
|
||||||
@import "./components/views/beacon/_StyledLiveBeaconIcon.scss";
|
@import "./components/views/beacon/_StyledLiveBeaconIcon.scss";
|
||||||
@import "./components/views/location/_LiveDurationDropdown.scss";
|
@import "./components/views/location/_LiveDurationDropdown.scss";
|
||||||
@import "./components/views/location/_LocationShareMenu.scss";
|
@import "./components/views/location/_LocationShareMenu.scss";
|
||||||
@import "./components/views/location/_MapError.scss";
|
@import "./components/views/location/_MapError.scss";
|
||||||
|
@import "./components/views/location/_Marker.scss";
|
||||||
@import "./components/views/location/_ShareDialogButtons.scss";
|
@import "./components/views/location/_ShareDialogButtons.scss";
|
||||||
@import "./components/views/location/_ShareType.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 "./components/views/spaces/_QuickThemeSwitcher.scss";
|
||||||
@import "./structures/_AutoHideScrollbar.scss";
|
@import "./structures/_AutoHideScrollbar.scss";
|
||||||
@import "./structures/_BackdropPanel.scss";
|
@import "./structures/_BackdropPanel.scss";
|
||||||
|
|
61
res/css/components/views/beacon/_BeaconStatus.scss
Normal file
|
@ -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;
|
||||||
|
}
|
79
res/css/components/views/beacon/_BeaconViewDialog.scss
Normal file
|
@ -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;
|
||||||
|
}
|
20
res/css/components/views/beacon/_LiveTimeRemaining.scss
Normal file
|
@ -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;
|
||||||
|
}
|
27
res/css/components/views/beacon/_OwnBeaconStatus.scss
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -39,12 +39,6 @@ limitations under the License.
|
||||||
font-size: $font-15px;
|
font-size: $font-15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_RoomLiveShareWarning_expiry {
|
|
||||||
color: $secondary-content;
|
|
||||||
font-size: $font-12px;
|
|
||||||
margin-right: $spacing-16;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomLiveShareWarning_spinner {
|
.mx_RoomLiveShareWarning_spinner {
|
||||||
margin-right: $spacing-16;
|
margin-right: $spacing-16;
|
||||||
}
|
}
|
||||||
|
@ -54,6 +48,10 @@ limitations under the License.
|
||||||
margin-left: $spacing-16;
|
margin-left: $spacing-16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_RoomLiveShareWarning_stopButton {
|
||||||
|
margin-left: $spacing-16;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_RoomLiveShareWarning_closeButtonIcon {
|
.mx_RoomLiveShareWarning_closeButtonIcon {
|
||||||
height: $font-18px;
|
height: $font-18px;
|
||||||
padding: $spacing-4;
|
padding: $spacing-4;
|
||||||
|
|
|
@ -33,3 +33,8 @@ limitations under the License.
|
||||||
background-color: $alert;
|
background-color: $alert;
|
||||||
border-color: $alert;
|
border-color: $alert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_StyledLiveBeaconIcon.mx_StyledLiveBeaconIcon_idle {
|
||||||
|
background-color: $quaternary-content;
|
||||||
|
border-color: $quaternary-content;
|
||||||
|
}
|
||||||
|
|
46
res/css/components/views/location/_Marker.scss
Normal file
|
@ -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;
|
||||||
|
}
|
45
res/css/components/views/location/_ZoomButtons.scss
Normal file
|
@ -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;
|
||||||
|
}
|
65
res/css/components/views/messages/_MBeaconBody.scss
Normal file
|
@ -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;
|
||||||
|
}
|
|
@ -251,7 +251,8 @@ $activeBorderColor: $primary-content;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
display: none;
|
display: none;
|
||||||
position: relative;
|
position: absolute;
|
||||||
|
right: 4px;
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
top: 3px;
|
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 */
|
/* root space buttons are bigger and not indented */
|
||||||
& > .mx_AutoHideScrollbar {
|
& > .mx_AutoHideScrollbar {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|
|
@ -108,26 +108,25 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Toast_title {
|
.mx_Toast_title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
column-gap: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
grid-column: 1 / 3;
|
|
||||||
grid-row: 1;
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: $font-15px;
|
font-size: $font-15px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
display: inline;
|
display: inline;
|
||||||
width: auto;
|
width: auto;
|
||||||
vertical-align: middle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
.mx_Toast_title_countIndicator {
|
||||||
padding-left: 8px;
|
|
||||||
float: right;
|
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
line-height: $font-22px;
|
line-height: $font-22px;
|
||||||
color: $secondary-content;
|
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 {
|
.mx_Toast_buttons {
|
||||||
float: right;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
column-gap: 5px;
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
.mx_AccessibleButton {
|
||||||
min-width: 96px;
|
min-width: 96px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton + .mx_AccessibleButton {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Toast_description {
|
.mx_Toast_description {
|
||||||
|
@ -157,10 +153,6 @@ limitations under the License.
|
||||||
margin: 4px 0 11px 0;
|
margin: 4px 0 11px 0;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,11 +147,6 @@ limitations under the License.
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-weight: normal;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,13 @@ limitations under the License.
|
||||||
padding: 0.5em 1em 0.5em 1em;
|
padding: 0.5em 1em 0.5em 1em;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ViewSource_details {
|
.mx_ViewSource_details {
|
||||||
margin-top: 0.8em;
|
margin-top: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_ViewSource_container {
|
||||||
|
max-width: calc(100% - 24px);
|
||||||
|
}
|
||||||
|
|
|
@ -88,13 +88,12 @@ limitations under the License.
|
||||||
div.mx_AccessibleButton_kind_link.mx_Login_forgot {
|
div.mx_AccessibleButton_kind_link.mx_Login_forgot {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
// style it as a link
|
|
||||||
font-size: inherit;
|
|
||||||
|
|
||||||
&.mx_AccessibleButton_disabled {
|
&.mx_AccessibleButton_disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Login_spinner {
|
.mx_Login_spinner {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
@ -90,6 +90,22 @@ limitations under the License.
|
||||||
mask-image: url('$(res)/img/element-icons/room/pin.svg');
|
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 {
|
.mx_MessageContextMenu_iconViewInRoom::before {
|
||||||
mask-image: url('$(res)/img/element-icons/view-in-room.svg');
|
mask-image: url('$(res)/img/element-icons/view-in-room.svg');
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,10 +58,6 @@ limitations under the License.
|
||||||
line-height: $font-15px;
|
line-height: $font-15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, .mx_AccessibleButton_kind_link {
|
a, .mx_AccessibleButton_kind_link {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
|
@ -21,9 +21,4 @@ limitations under the License.
|
||||||
line-height: $font-20px;
|
line-height: $font-20px;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,49 +48,10 @@ limitations under the License.
|
||||||
background-color: $dialog-close-external-color;
|
background-color: $dialog-close-external-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.mx_MLocationBody {
|
|
||||||
position: absolute;
|
.mx_LocationViewDialog_map {
|
||||||
|
width: 80vw;
|
||||||
.mx_MLocationBody_map {
|
height: 80vh;
|
||||||
width: 80vw;
|
border-radius: 8px;
|
||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,84 +22,99 @@ limitations under the License.
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.mx_RoomSettingsDialog_BridgeList li {
|
li {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 5px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
border-width: 1px 1px;
|
|
||||||
border-color: $primary-hairline-color;
|
|
||||||
border-style: solid;
|
|
||||||
border-radius: 5px;
|
|
||||||
|
|
||||||
.column-icon {
|
&.mx_RoomSettingsDialog_BridgeList_listItem {
|
||||||
float: left;
|
display: flex;
|
||||||
padding-right: 10px;
|
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-radius: 5px;
|
||||||
border: 1px solid $input-darker-bg-color;
|
|
||||||
}
|
|
||||||
|
|
||||||
.noProtocolIcon {
|
.mx_RoomSettingsDialog_column_icon {
|
||||||
width: 48px;
|
.mx_RoomSettingsDialog_protocolIcon,
|
||||||
height: 48px;
|
.mx_RoomSettingsDialog_protocolIcon span,
|
||||||
background: $input-darker-bg-color;
|
.mx_RoomSettingsDialog_noProtocolIcon {
|
||||||
border-radius: 5px;
|
box-sizing: border-box;
|
||||||
}
|
border-radius: 5px;
|
||||||
|
border: 1px solid $input-darker-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
.protocol-icon {
|
.mx_RoomSettingsDialog_noProtocolIcon,
|
||||||
float: left;
|
.mx_RoomSettingsDialog_protocolIcon img {
|
||||||
margin-right: 5px;
|
border-radius: 5px;
|
||||||
img {
|
}
|
||||||
border-radius: 5px;
|
|
||||||
border-width: 1px 1px;
|
.mx_RoomSettingsDialog_noProtocolIcon {
|
||||||
border-color: $primary-hairline-color;
|
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 {
|
.mx_RoomSettingsDialog_column_data {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 85%;
|
width: 85%;
|
||||||
|
|
||||||
> h3 {
|
.mx_RoomSettingsDialog_column_data_details,
|
||||||
margin-top: 0px;
|
.mx_RoomSettingsDialog_column_data_metadata,
|
||||||
margin-bottom: 0px;
|
.mx_RoomSettingsDialog_column_data_metadata li,
|
||||||
font-size: 16pt;
|
.mx_RoomSettingsDialog_column_data_protocolName {
|
||||||
color: $primary-content;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
> * {
|
.mx_RoomSettingsDialog_column_data_details,
|
||||||
margin-top: 4px;
|
.mx_RoomSettingsDialog_column_data_metadata {
|
||||||
margin-bottom: 0;
|
margin-top: $spacing-4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-channel-details {
|
.mx_RoomSettingsDialog_column_data_metadata li {
|
||||||
color: $primary-content;
|
margin-top: $spacing-8;
|
||||||
font-weight: 600;
|
}
|
||||||
|
|
||||||
.channel {
|
.mx_RoomSettingsDialog_column_data_protocolName {
|
||||||
margin-left: 5px;
|
margin-top: 0;
|
||||||
}
|
font-size: 16pt;
|
||||||
}
|
color: $primary-content;
|
||||||
|
}
|
||||||
|
|
||||||
.metadata {
|
.mx_RoomSettingsDialog_workspace_channel_details {
|
||||||
color: $muted-fg-color;
|
color: $primary-content;
|
||||||
margin-bottom: 0;
|
font-weight: $font-semi-bold;
|
||||||
overflow-y: visible;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: normal;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
> li {
|
.mx_RoomSettingsDialog_channel {
|
||||||
padding: 0;
|
margin-inline-start: 5px;
|
||||||
border: 0;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_RoomSettingsDialog_metadata {
|
||||||
|
color: $muted-fg-color;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow-y: visible;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: normal;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_AccessibleButton_hasKind {
|
.mx_AccessibleButton_hasKind {
|
||||||
&.mx_AccessibleButton_kind_link {
|
&.mx_AccessibleButton_kind_link {
|
||||||
|
font-size: $font-14px;
|
||||||
margin: 7px 18px;
|
margin: 7px 18px;
|
||||||
|
|
||||||
&.mx_SettingsTab_showAdvanced {
|
&.mx_SettingsTab_showAdvanced {
|
||||||
|
|
|
@ -241,7 +241,6 @@ limitations under the License.
|
||||||
.mx_SpotlightDialog_recentSearches > h4 > .mx_AccessibleButton_kind_link {
|
.mx_SpotlightDialog_recentSearches > h4 > .mx_AccessibleButton_kind_link {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
float: right;
|
float: right;
|
||||||
font-weight: normal;
|
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
line-height: $font-15px;
|
line-height: $font-15px;
|
||||||
color: $secondary-content;
|
color: $secondary-content;
|
||||||
|
|
|
@ -39,6 +39,7 @@ limitations under the License.
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: $font-14px;
|
font-size: $font-14px;
|
||||||
border: none; // override default <button /> styles
|
border: none; // override default <button /> styles
|
||||||
|
word-break: keep-all; // prevent button text in Chinese/Japanese/Korean (CJK) from being collapsed
|
||||||
|
|
||||||
&.mx_AccessibleButton_kind_primary_sm,
|
&.mx_AccessibleButton_kind_primary_sm,
|
||||||
&.mx_AccessibleButton_kind_danger_sm,
|
&.mx_AccessibleButton_kind_danger_sm,
|
||||||
|
@ -130,14 +131,20 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_AccessibleButton_kind_link {
|
&.mx_AccessibleButton_kind_link,
|
||||||
|
&.mx_AccessibleButton_kind_link_inline {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
|
font-size: inherit;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_AccessibleButton_kind_link {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_AccessibleButton_kind_link_inline {
|
&.mx_AccessibleButton_kind_link_inline {
|
||||||
color: $accent;
|
display: inline;
|
||||||
font-size: inherit;
|
|
||||||
padding: 0 2px;
|
padding: 0 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,14 +18,17 @@ limitations under the License.
|
||||||
.mx_CopyableText {
|
.mx_CopyableText {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
border-radius: 5px;
|
|
||||||
border: solid 1px $light-fg-color;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-top: 10px;
|
|
||||||
padding: 10px;
|
|
||||||
width: max-content;
|
width: max-content;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
|
|
||||||
|
&.mx_CopyableText_border {
|
||||||
|
border-radius: 5px;
|
||||||
|
border: solid 1px $light-fg-color;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CopyableText_copyButton {
|
.mx_CopyableText_copyButton {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 20px;
|
width: 20px;
|
||||||
|
|
|
@ -29,7 +29,9 @@ limitations under the License.
|
||||||
margin-left: 16px; // distance from <Field>
|
margin-left: 16px; // distance from <Field>
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_Field, .mx_Field input, .mx_AccessibleButton {
|
.mx_Field,
|
||||||
|
.mx_Field input,
|
||||||
|
.mx_AccessibleButton {
|
||||||
// So they look related to each other by feeling the same
|
// So they look related to each other by feeling the same
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
@ -39,39 +41,48 @@ limitations under the License.
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
margin-top: 12px; // this plus 12px from the tags makes 24px from the input
|
margin-top: 12px; // this plus 12px from the tags makes 24px from the input
|
||||||
|
}
|
||||||
|
|
||||||
.mx_TagComposer_tag {
|
.mx_Tag {
|
||||||
padding: 6px 8px 8px 12px;
|
margin-right: 12px;
|
||||||
position: relative;
|
margin-top: 12px;
|
||||||
margin-right: 12px;
|
}
|
||||||
margin-top: 12px;
|
}
|
||||||
|
|
||||||
// Cheaty way to get an opacified variable colour background
|
.mx_Tag {
|
||||||
&::before {
|
|
||||||
content: '';
|
|
||||||
border-radius: 20px;
|
|
||||||
background-color: $tertiary-content;
|
|
||||||
opacity: 0.15;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
// Pass through the pointer otherwise we have effectively put a whole div
|
font-size: $font-15px;
|
||||||
// on top of the component, which makes it hard to interact with buttons.
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AccessibleButton {
|
display: inline-flex;
|
||||||
background-image: url('$(res)/img/subtract.svg');
|
align-items: center;
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
gap: 8px;
|
||||||
margin-left: 8px;
|
padding: 8px;
|
||||||
display: inline-block;
|
border-radius: 8px;
|
||||||
|
|
||||||
|
color: $primary-content;
|
||||||
|
background: $quinary-content;
|
||||||
|
|
||||||
|
>svg:first-child {
|
||||||
|
width: 1em;
|
||||||
|
color: $secondary-content;
|
||||||
|
transform: scale(1.25);
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Tag_delete {
|
||||||
|
border-radius: 50%;
|
||||||
|
text-align: center;
|
||||||
|
width: 1.066666em; // 16px;
|
||||||
|
height: 1.066666em;
|
||||||
|
line-height: 1em;
|
||||||
|
color: $secondary-content;
|
||||||
|
background: $system;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: .5em;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,39 +55,6 @@ limitations under the License.
|
||||||
.maplibregl-user-location-dot {
|
.maplibregl-user-location-dot {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MLocationBody_markerBorder {
|
|
||||||
width: 31px;
|
|
||||||
height: 31px;
|
|
||||||
border-radius: 50%;
|
|
||||||
filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.2));
|
|
||||||
background-color: currentColor;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MLocationBody_pointer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -3px;
|
|
||||||
left: 11px;
|
|
||||||
width: 9px;
|
|
||||||
height: 5px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
mask-image: url('$(res)/img/location/pointer.svg');
|
|
||||||
mask-position: center;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: 9px;
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 9px;
|
|
||||||
height: 5px;
|
|
||||||
position: absolute;
|
|
||||||
background-color: currentColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_LocationPicker_footer {
|
.mx_LocationPicker_footer {
|
||||||
|
@ -106,11 +73,6 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MLocationBody_markerIcon {
|
|
||||||
color: white;
|
|
||||||
height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_LocationPicker_pinText {
|
.mx_LocationPicker_pinText {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $spacing-16;
|
top: $spacing-16;
|
||||||
|
@ -135,11 +97,3 @@ limitations under the License.
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
// live marker color is set by user color class
|
|
||||||
// generated from userid
|
|
||||||
// others are $accent
|
|
||||||
.mx_MLocationBody_marker-Self,
|
|
||||||
.mx_MLocationBody_marker-Pin {
|
|
||||||
color: $accent;
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,22 +19,24 @@ limitations under the License.
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.mx_CallEvent {
|
.mx_CallEvent {
|
||||||
position: relative;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: $spacing-4 0;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
margin: $spacing-4 0;
|
||||||
|
padding: $spacing-12 $spacing-24;
|
||||||
|
box-sizing: border-box;
|
||||||
background-color: $dark-panel-bg-color;
|
background-color: $dark-panel-bg-color;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
width: 65%;
|
width: 65%;
|
||||||
box-sizing: border-box;
|
height: fit-content;
|
||||||
height: 60px;
|
|
||||||
margin: 4px 0;
|
|
||||||
|
|
||||||
.mx_CallEvent_iconButton {
|
.mx_CallEvent_iconButton {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin-right: 8px;
|
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -62,6 +64,13 @@ limitations under the License.
|
||||||
.mx_CallEvent_content_button_answer span::before {
|
.mx_CallEvent_content_button_answer span::before {
|
||||||
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
mask-image: url('$(res)/img/element-icons/call/voice-call.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_CallEvent_rejected,
|
||||||
|
&.mx_CallEvent_noAnswer {
|
||||||
|
.mx_CallEvent_type_icon::before {
|
||||||
|
mask-image: url('$(res)/img/voip/declined-voice.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_CallEvent_video {
|
&.mx_CallEvent_video {
|
||||||
|
@ -70,44 +79,49 @@ limitations under the License.
|
||||||
.mx_CallEvent_content_button_answer span::before {
|
.mx_CallEvent_content_button_answer span::before {
|
||||||
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
|
mask-image: url('$(res)/img/element-icons/call/video-call.svg');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_CallEvent_rejected,
|
||||||
|
&.mx_CallEvent_noAnswer {
|
||||||
|
.mx_CallEvent_type_icon::before {
|
||||||
|
mask-image: url('$(res)/img/voip/declined-video.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.mx_CallEvent_voice.mx_CallEvent_missed .mx_CallEvent_type_icon::before {
|
&.mx_CallEvent_missed {
|
||||||
mask-image: url('$(res)/img/voip/missed-voice.svg');
|
&.mx_CallEvent_voice {
|
||||||
}
|
.mx_CallEvent_type_icon::before {
|
||||||
|
mask-image: url('$(res)/img/voip/missed-voice.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.mx_CallEvent_video.mx_CallEvent_missed .mx_CallEvent_type_icon::before {
|
&.mx_CallEvent_video {
|
||||||
mask-image: url('$(res)/img/voip/missed-video.svg');
|
.mx_CallEvent_type_icon::before {
|
||||||
}
|
mask-image: url('$(res)/img/voip/missed-video.svg');
|
||||||
|
}
|
||||||
&.mx_CallEvent_voice.mx_CallEvent_rejected .mx_CallEvent_type_icon::before,
|
}
|
||||||
&.mx_CallEvent_voice.mx_CallEvent_noAnswer .mx_CallEvent_type_icon::before {
|
|
||||||
mask-image: url('$(res)/img/voip/declined-voice.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mx_CallEvent_video.mx_CallEvent_rejected .mx_CallEvent_type_icon::before,
|
|
||||||
&.mx_CallEvent_video.mx_CallEvent_noAnswer .mx_CallEvent_type_icon::before {
|
|
||||||
mask-image: url('$(res)/img/voip/declined-video.svg');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallEvent_info {
|
.mx_CallEvent_info {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
margin-left: 12px;
|
width: fit-content;
|
||||||
min-width: 0;
|
max-width: 100%;
|
||||||
|
|
||||||
.mx_CallEvent_info_basic {
|
.mx_CallEvent_info_basic {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
gap: $spacing-4;
|
||||||
margin-left: 10px; // To match mx_CallEvent
|
margin-left: 10px; // To match mx_CallEvent
|
||||||
|
margin-right: 10px;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
|
||||||
.mx_CallEvent_sender {
|
.mx_CallEvent_sender {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
line-height: 1.8rem;
|
line-height: 1.8rem;
|
||||||
margin-bottom: 3px;
|
margin-bottom: $spacing-4;
|
||||||
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
@ -115,12 +129,12 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallEvent_type {
|
.mx_CallEvent_type {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: $secondary-content;
|
color: $secondary-content;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
line-height: $font-13px;
|
line-height: $font-13px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.mx_CallEvent_type_icon {
|
.mx_CallEvent_type_icon {
|
||||||
height: 13px;
|
height: 13px;
|
||||||
|
@ -143,16 +157,17 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_CallEvent_content {
|
.mx_CallEvent_content {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: $secondary-content;
|
color: $secondary-content;
|
||||||
margin-right: 16px;
|
gap: $spacing-12; // See mx_IncomingCallToast_buttons
|
||||||
gap: 12px; // See mx_IncomingCallToast_buttons
|
margin-inline-start: 42px; // avatar (32px) + mx_CallEvent_info_basic margin (10px)
|
||||||
min-width: max-content;
|
word-break: break-word;
|
||||||
|
max-width: fit-content;
|
||||||
|
|
||||||
.mx_CallEvent_content_button {
|
.mx_CallEvent_content_button {
|
||||||
@mixin CallButton;
|
@mixin CallButton;
|
||||||
padding: 0 12px;
|
padding: 0 $spacing-12;
|
||||||
|
|
||||||
span::before {
|
span::before {
|
||||||
mask-size: 16px;
|
mask-size: 16px;
|
||||||
|
@ -162,8 +177,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallEvent_content_button_reject span::before {
|
.mx_CallEvent_content_button_reject {
|
||||||
mask-image: url('$(res)/img/element-icons/call/hangup.svg');
|
span::before {
|
||||||
|
mask-image: url('$(res)/img/element-icons/call/hangup.svg');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallEvent_content_tooltip {
|
.mx_CallEvent_content_tooltip {
|
||||||
|
@ -171,16 +188,12 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MessageTimestamp {
|
|
||||||
margin-left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.mx_CallEvent_narrow {
|
&.mx_CallEvent_narrow {
|
||||||
height: unset;
|
|
||||||
width: 290px;
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: unset;
|
align-items: unset;
|
||||||
gap: 16px;
|
gap: $spacing-4 $spacing-16;
|
||||||
|
height: unset;
|
||||||
|
min-width: 290px;
|
||||||
|
|
||||||
.mx_CallEvent_iconButton {
|
.mx_CallEvent_iconButton {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -194,18 +207,36 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_CallEvent_info {
|
.mx_CallEvent_info {
|
||||||
align-items: unset;
|
align-items: unset;
|
||||||
margin-top: 12px;
|
|
||||||
margin-right: 12px;
|
|
||||||
|
|
||||||
.mx_CallEvent_sender {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_CallEvent_content {
|
|
||||||
margin-left: 54px; // mx_CallEvent margin (12px) + avatar (32px) + mx_CallEvent_info_basic margin (10px)
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile[data-layout="bubble"] {
|
||||||
|
.mx_EventTile_e2eIcon + .mx_CallEvent_wrapper {
|
||||||
|
.mx_CallEvent {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
// 5px (gap) + 14px (e2e icon size * mask-size) + 9px (margin-left of e2e icon)
|
||||||
|
right: calc(5px + 14px + 9px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_leftAlignedBubble {
|
||||||
|
.mx_CallEvent_wrapper {
|
||||||
|
.mx_CallEvent {
|
||||||
|
&.mx_CallEvent_narrow {
|
||||||
|
gap: $spacing-8 $spacing-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_IRCLayout {
|
||||||
|
.mx_CallEvent_wrapper {
|
||||||
|
.mx_CallEvent {
|
||||||
|
margin-inline-start: $spacing-4; // display green line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -38,6 +38,12 @@ $timeline-image-border-radius: 8px;
|
||||||
// Necessary for the border radius to apply correctly to the placeholder
|
// Necessary for the border radius to apply correctly to the placeholder
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
contain: paint;
|
contain: paint;
|
||||||
|
|
||||||
|
min-height: $font-44px;
|
||||||
|
min-width: $font-44px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MImageBody_thumbnail {
|
.mx_MImageBody_thumbnail {
|
||||||
|
|
|
@ -22,56 +22,6 @@ limitations under the License.
|
||||||
|
|
||||||
border-radius: $timeline-image-border-radius;
|
border-radius: $timeline-image-border-radius;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MLocationBody_markerBorder {
|
|
||||||
width: 31px;
|
|
||||||
height: 31px;
|
|
||||||
border-radius: 50%;
|
|
||||||
filter: drop-shadow(0px 3px 5px rgba(0, 0, 0, 0.2));
|
|
||||||
background-color: $accent;
|
|
||||||
|
|
||||||
// See _LocationPicker.scss
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.mx_BaseAvatar {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MLocationBody_pointer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: -3px;
|
|
||||||
left: 11px;
|
|
||||||
width: 9px;
|
|
||||||
height: 5px;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
mask-image: url('$(res)/img/location/pointer.svg');
|
|
||||||
mask-position: center;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: 9px;
|
|
||||||
content: '';
|
|
||||||
display: inline-block;
|
|
||||||
width: 9px;
|
|
||||||
height: 5px;
|
|
||||||
position: absolute;
|
|
||||||
background-color: $accent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MLocationBody_markerContents {
|
|
||||||
background-color: $location-marker-color;
|
|
||||||
margin: 0;
|
|
||||||
width: 31px;
|
|
||||||
height: 31px;
|
|
||||||
mask-repeat: no-repeat;
|
|
||||||
mask-size: 16px;
|
|
||||||
mask-position: center;
|
|
||||||
mask-image: url('$(res)/img/element-icons/location.svg');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In the timeline, we fit the width of the container */
|
/* In the timeline, we fit the width of the container */
|
||||||
|
|
|
@ -165,6 +165,10 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_GenericEventListSummary > .mx_EventTile_line {
|
||||||
|
padding-left: 30px !important; // Override main timeline styling - align summary text with message text
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout=bubble]) {
|
.mx_EventTile:not([data-layout=bubble]) {
|
||||||
.mx_EventTile_e2eIcon {
|
.mx_EventTile_e2eIcon {
|
||||||
left: 8px;
|
left: 8px;
|
||||||
|
@ -212,19 +216,21 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
// handling for hidden events (e.g reactions) in the thread view
|
// handling for hidden events (e.g reactions) in the thread view
|
||||||
&.mx_ThreadView .mx_GenericEventListSummary_unstyledList .mx_EventTile_info {
|
&.mx_ThreadView .mx_EventTile_info {
|
||||||
|
padding-top: 0 !important; // override main timeline padding
|
||||||
|
|
||||||
.mx_EventTile_line {
|
.mx_EventTile_line {
|
||||||
padding-left: 0 !important; // override main timeline padding
|
padding-left: 0 !important; // override main timeline padding
|
||||||
|
|
||||||
.mx_EventTile_content {
|
.mx_EventTile_content {
|
||||||
margin-left: 54px; // align with text
|
margin-left: 48px; // align with text
|
||||||
width: calc(100% - 54px - 8px); // match width of parent
|
width: calc(100% - 48px - 8px); // match width of parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_avatar {
|
.mx_EventTile_avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 36px !important; // override main timeline positioning
|
left: 30px !important; // override main timeline positioning
|
||||||
z-index: 9; // position above the hover styling
|
z-index: 9; // position above the hover styling
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +243,7 @@ limitations under the License.
|
||||||
text-align: left;
|
text-align: left;
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: flex-end;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
|
|
@ -39,6 +39,7 @@ limitations under the License.
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative; // offset parent for jump to bottom button
|
position: relative; // offset parent for jump to bottom button
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AutoHideScrollbar {
|
.mx_AutoHideScrollbar {
|
||||||
|
@ -54,29 +55,61 @@ limitations under the License.
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_EventTile_line {
|
.mx_EventTile:not([data-layout="bubble"]) {
|
||||||
padding-left: 36px;
|
.mx_EventTile_line {
|
||||||
padding-right: 36px;
|
padding-left: 36px;
|
||||||
|
padding-right: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ReactionsRow {
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
// See margin setting of ReactionsRow on _EventTile.scss
|
||||||
|
margin-left: 36px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_ThreadInfo {
|
||||||
|
margin-left: 36px;
|
||||||
|
margin-right: 0;
|
||||||
|
max-width: min(calc(100% - 36px), 600px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_avatar {
|
||||||
|
top: 12px;
|
||||||
|
left: -3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_MessageTimestamp {
|
||||||
|
right: -4px;
|
||||||
|
left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_msgOption {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.mx_EventTile_info {
|
||||||
|
.mx_EventTile_line {
|
||||||
|
padding-left: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_avatar {
|
||||||
|
left: 18px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_ReactionsRow {
|
.mx_GroupLayout {
|
||||||
padding-left: 36px;
|
.mx_EventTile {
|
||||||
padding-right: 36px;
|
> .mx_DisambiguatedProfile {
|
||||||
}
|
margin-left: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_ThreadInfo {
|
.mx_EventTile_line {
|
||||||
margin-left: 36px;
|
padding-bottom: 8px;
|
||||||
margin-right: 0;
|
}
|
||||||
max-width: min(calc(100% - 36px), 600px);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.mx_GroupLayout .mx_EventTile > .mx_DisambiguatedProfile {
|
|
||||||
margin-left: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_EventTile_avatar {
|
|
||||||
top: 12px;
|
|
||||||
left: -3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_CallEvent_wrapper {
|
.mx_CallEvent_wrapper {
|
||||||
|
@ -87,36 +120,15 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_MessageTimestamp {
|
|
||||||
right: -4px;
|
|
||||||
left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]) .mx_EventTile_msgOption {
|
|
||||||
margin-right: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_GenericEventListSummary:not([data-layout=bubble]) .mx_EventTile_line,
|
.mx_GenericEventListSummary:not([data-layout=bubble]) .mx_EventTile_line,
|
||||||
.mx_GenericEventListSummary:not([data-layout=bubble]) > .mx_GenericEventListSummary_unstyledList > .mx_EventTile_info .mx_EventTile_avatar ~ .mx_EventTile_line {
|
.mx_GenericEventListSummary:not([data-layout=bubble]) > .mx_GenericEventListSummary_unstyledList > .mx_EventTile_info .mx_EventTile_avatar ~ .mx_EventTile_line {
|
||||||
padding-left: 36px;
|
padding-left: 36px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_GroupLayout .mx_EventTile .mx_EventTile_line {
|
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile_readAvatars {
|
.mx_EventTile_readAvatars {
|
||||||
top: -10px;
|
top: -10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]).mx_EventTile_info .mx_EventTile_line {
|
|
||||||
padding-left: 36px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_EventTile:not([data-layout="bubble"]).mx_EventTile_info .mx_EventTile_avatar {
|
|
||||||
left: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_WhoIsTypingTile {
|
.mx_WhoIsTypingTile {
|
||||||
margin-left: -12px; // undo padding on the message list
|
margin-left: -12px; // undo padding on the message list
|
||||||
}
|
}
|
||||||
|
@ -124,4 +136,12 @@ limitations under the License.
|
||||||
.mx_WhoIsTypingTile_avatars {
|
.mx_WhoIsTypingTile_avatars {
|
||||||
flex-basis: 48px; // 12 (padding on message list) + 36 (padding on event lines)
|
flex-basis: 48px; // 12 (padding on message list) + 36 (padding on event lines)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mx_BaseCard {
|
||||||
|
// For a chat timeline on the right panel when the widget is maximised
|
||||||
|
// TODO: rename ThreadPanel
|
||||||
|
&.mx_ThreadPanel {
|
||||||
|
padding-right: 8px; // .mx_RightPanel padding
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,6 +114,11 @@ limitations under the License.
|
||||||
padding-right: 48px;
|
padding-right: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MImageBody_thumbnail_container {
|
||||||
|
min-height: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
|
||||||
|
min-width: calc(1.8rem + var(--gutterSize) + var(--gutterSize));
|
||||||
|
}
|
||||||
|
|
||||||
.mx_CallEvent {
|
.mx_CallEvent {
|
||||||
background-color: unset;
|
background-color: unset;
|
||||||
|
|
||||||
|
@ -130,7 +135,8 @@ limitations under the License.
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-bottom-right-radius: var(--cornerRadius) !important;
|
border-bottom-right-radius: var(--cornerRadius) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +161,8 @@ limitations under the License.
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-bottom-left-radius: var(--cornerRadius) !important;
|
border-bottom-left-radius: var(--cornerRadius) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,7 +307,8 @@ limitations under the License.
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -311,7 +319,8 @@ limitations under the License.
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-bottom-left-radius: var(--cornerRadius);
|
border-bottom-left-radius: var(--cornerRadius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,7 +332,8 @@ limitations under the License.
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -334,7 +344,8 @@ limitations under the License.
|
||||||
.mx_MVideoBody .mx_MVideoBody_container,
|
.mx_MVideoBody .mx_MVideoBody_container,
|
||||||
.mx_MImageBody::before,
|
.mx_MImageBody::before,
|
||||||
.mx_MediaBody,
|
.mx_MediaBody,
|
||||||
.mx_MLocationBody_map {
|
.mx_MLocationBody_map,
|
||||||
|
.mx_MBeaconBody {
|
||||||
border-bottom-right-radius: var(--cornerRadius);
|
border-bottom-right-radius: var(--cornerRadius);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -317,6 +317,7 @@ $left-gutter: 64px;
|
||||||
.mx_EventTile_line {
|
.mx_EventTile_line {
|
||||||
/* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */
|
/* ideally should be 100px, but 95px gives us a max thumbnail size of 800x600, which is nice */
|
||||||
margin-right: 110px;
|
margin-right: 110px;
|
||||||
|
min-height: $font-14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ThreadInfo {
|
.mx_ThreadInfo {
|
||||||
|
@ -693,6 +694,16 @@ $left-gutter: 64px;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inverse of the above to *disable* the animation on any indicators. This approach
|
||||||
|
// is less pretty, but is easier to target because otherwise we need to define the
|
||||||
|
// animation for when it's shown which means duplicating the style definition in
|
||||||
|
// multiple places.
|
||||||
|
.mx_EventTile:not(:hover):not(.mx_EventTile_actionBarFocused):not([data-whatinput='keyboard'] :focus-within):not(.focus-visible:focus-within) {
|
||||||
|
.mx_MessageActionBar .mx_Indicator {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media only screen and (max-width: 480px) {
|
@media only screen and (max-width: 480px) {
|
||||||
|
|
||||||
.mx_EventTile_line,
|
.mx_EventTile_line,
|
||||||
|
@ -762,7 +773,8 @@ $left-gutter: 64px;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
width: 60px;
|
width: 60px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
background: linear-gradient(270deg, $system 52.6%, transparent 100%);
|
// XXX: We use `$system-transparent` instead of `transparent` to work around a Safari <15.4 bug
|
||||||
|
background: linear-gradient(270deg, $system 50%, $system-transparent 100%);
|
||||||
|
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(60px);
|
transform: translateX(60px);
|
||||||
|
@ -1004,6 +1016,7 @@ $threadInfoLineHeight: calc(2 * $font-12px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile[data-layout=group] {
|
.mx_EventTile[data-layout=group] {
|
||||||
|
$spacing-start: 48px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.mx_EventTile_content,
|
.mx_EventTile_content,
|
||||||
|
@ -1011,8 +1024,9 @@ $threadInfoLineHeight: calc(2 * $font-12px);
|
||||||
.mx_RedactedBody,
|
.mx_RedactedBody,
|
||||||
.mx_UnknownBody,
|
.mx_UnknownBody,
|
||||||
.mx_MPollBody,
|
.mx_MPollBody,
|
||||||
.mx_ReplyChain_wrapper {
|
.mx_ReplyChain_wrapper,
|
||||||
margin-left: 48px;
|
.mx_ReactionsRow {
|
||||||
|
margin-left: $spacing-start;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
|
|
||||||
.mx_EventTile_content,
|
.mx_EventTile_content,
|
||||||
|
@ -1023,11 +1037,6 @@ $threadInfoLineHeight: calc(2 * $font-12px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_ReactionsRow {
|
|
||||||
margin-left: 36px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_MessageTimestamp {
|
.mx_MessageTimestamp {
|
||||||
top: 2px !important;
|
top: 2px !important;
|
||||||
width: auto;
|
width: auto;
|
||||||
|
@ -1051,10 +1060,14 @@ $threadInfoLineHeight: calc(2 * $font-12px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_EventTile_mediaLine {
|
||||||
|
padding-inline-start: $spacing-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_mediaLine {
|
.mx_EventTile_mediaLine {
|
||||||
padding-left: 36px !important;
|
padding-left: 36px;
|
||||||
padding-right: 50px;
|
padding-right: 50px;
|
||||||
|
|
||||||
.mx_MImageBody {
|
.mx_MImageBody {
|
||||||
|
|
|
@ -23,10 +23,6 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_NewRoomIntro_buttons {
|
.mx_NewRoomIntro_buttons {
|
||||||
margin-top: 28px;
|
margin-top: 28px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -92,8 +92,6 @@ limitations under the License.
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
.mx_AccessibleButton_kind_link {
|
||||||
margin-left: 12px;
|
margin-left: 12px;
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ limitations under the License.
|
||||||
margin-bottom: 28px;
|
margin-bottom: 28px;
|
||||||
|
|
||||||
> .mx_AccessibleButton_kind_link {
|
> .mx_AccessibleButton_kind_link {
|
||||||
|
font-size: $font-14px;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,10 +50,6 @@ limitations under the License.
|
||||||
font-size: $font-12px;
|
font-size: $font-12px;
|
||||||
line-height: $font-15px;
|
line-height: $font-15px;
|
||||||
margin-right: 32px;
|
margin-right: 32px;
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,9 +32,5 @@ limitations under the License.
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ limitations under the License.
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: auto 18px;
|
margin: auto 18px;
|
||||||
color: #368bd6;
|
color: #368bd6;
|
||||||
|
font-size: $font-14px; // See _SpaceSettingsDialog.scss
|
||||||
}
|
}
|
||||||
|
|
||||||
> .mx_SpaceBasicSettings_avatar_remove {
|
> .mx_SpaceBasicSettings_avatar_remove {
|
||||||
|
|
|
@ -93,10 +93,6 @@ $spacePanelWidth: 68px;
|
||||||
width: min-content;
|
width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_AccessibleButton_kind_link {
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_AccessibleButton_disabled {
|
.mx_AccessibleButton_disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +114,5 @@ $spacePanelWidth: 68px;
|
||||||
.mx_AccessibleButton_kind_link {
|
.mx_AccessibleButton_kind_link {
|
||||||
color: $accent;
|
color: $accent;
|
||||||
position: relative;
|
position: relative;
|
||||||
font-size: inherit;
|
|
||||||
line-height: inherit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 8.00001C2 7.56779 2.35038 7.21741 2.78261 7.21741L13.2173 7.21741C13.6496 7.21741 13.9999 7.56779 13.9999 8.00001C13.9999 8.43223 13.6496 8.78262 13.2173 8.78262L2.78261 8.78262C2.35038 8.78262 2 8.43223 2 8.00001Z" fill="#17191C"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 8.00001C2 7.56779 2.35038 7.21741 2.78261 7.21741L13.2173 7.21741C13.6496 7.21741 13.9999 7.56779 13.9999 8.00001C13.9999 8.43223 13.6496 8.78262 13.2173 8.78262L2.78261 8.78262C2.35038 8.78262 2 8.43223 2 8.00001Z" fill="currentColor"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 388 B After Width: | Height: | Size: 393 B |
|
@ -1,3 +1,3 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.78269 2.78285C8.78269 2.35063 8.43231 2.00024 8.00009 2.00024C7.56787 2.00024 7.21748 2.35063 7.21748 2.78285V7.21748L2.78285 7.21748C2.35063 7.21748 2.00024 7.56787 2.00024 8.00009C2.00024 8.43231 2.35063 8.78269 2.78285 8.78269L7.21748 8.7827V13.2176C7.21748 13.6498 7.56787 14.0002 8.00009 14.0002C8.43231 14.0002 8.7827 13.6498 8.7827 13.2176V8.7827L13.2176 8.7827C13.6498 8.7827 14.0002 8.43231 14.0002 8.00009C14.0002 7.56787 13.6498 7.21749 13.2176 7.21749L8.78269 7.21748V2.78285Z" fill="#17191C"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.78269 2.78285C8.78269 2.35063 8.43231 2.00024 8.00009 2.00024C7.56787 2.00024 7.21748 2.35063 7.21748 2.78285V7.21748L2.78285 7.21748C2.35063 7.21748 2.00024 7.56787 2.00024 8.00009C2.00024 8.43231 2.35063 8.78269 2.78285 8.78269L7.21748 8.7827V13.2176C7.21748 13.6498 7.56787 14.0002 8.00009 14.0002C8.43231 14.0002 8.7827 13.6498 8.7827 13.2176V8.7827L13.2176 8.7827C13.6498 8.7827 14.0002 8.43231 14.0002 8.00009C14.0002 7.56787 13.6498 7.21749 13.2176 7.21749L8.78269 7.21748V2.78285Z" fill="currentColor"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 662 B After Width: | Height: | Size: 667 B |
|
@ -1,5 +1,4 @@
|
||||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.393 1.00573C11.9421 1.06434 12.3398 1.55703 12.2812 2.1062L12.0245 4.51168H13.8482C14.4005 4.51168 14.8482 4.95939 14.8482 5.51168C14.8482 6.06396 14.4005 6.51168 13.8482 6.51168H13.0089C12.5534 5.89782 11.8231 5.5 11 5.5C10.1769 5.5 9.44663 5.89782 8.9911 6.51168H6.36673L6.05542 9.42868C5.70806 9.85802 5.5 10.4047 5.5 11C5.5 11.4052 5.5964 11.7879 5.76753 12.1264L5.55616 14.107C5.49755 14.6562 5.00485 15.0538 4.45569 14.9952C3.90652 14.9366 3.50884 14.4439 3.56745 13.8948L3.79318 11.7796H2.0005C1.44822 11.7796 1.0005 11.3319 1.0005 10.7796C1.0005 10.2273 1.44822 9.77962 2.0005 9.77962H4.00661L4.35537 6.51168H2.64188C2.0896 6.51168 1.64188 6.06396 1.64188 5.51168C1.64188 4.95939 2.0896 4.51168 2.64188 4.51168H4.56881L4.84817 1.89397C4.90677 1.3448 5.39947 0.947122 5.94864 1.00573C6.49781 1.06434 6.89548 1.55703 6.83688 2.1062L6.58016 4.51168H10.0131L10.2925 1.89397C10.3511 1.3448 10.8438 0.947122 11.393 1.00573Z" fill="#737D8C"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.393 1.00262C11.9421 1.06122 12.3398 1.55392 12.2812 2.10309L12.0245 4.50857H13.8482C14.4004 4.50857 14.8482 4.95628 14.8482 5.50857C14.8482 6.06085 14.4004 6.50857 13.8482 6.50857H13.0598C12.6805 5.45391 11.6713 4.69975 10.486 4.69975C9.30072 4.69975 8.29156 5.45391 7.91221 6.50857H6.36671L6.19745 8.09457C5.28778 8.53869 4.66113 9.47279 4.66113 10.5533C4.66113 11.4255 5.06957 12.2024 5.70563 12.7031L5.55614 14.1039C5.49753 14.6531 5.00484 15.0507 4.45567 14.9921C3.9065 14.9335 3.50883 14.4408 3.56743 13.8917L3.79316 11.7765H2.00049C1.4482 11.7765 1.00049 11.3288 1.00049 10.7765C1.00049 10.2242 1.4482 9.77651 2.00049 9.77651H4.0066L4.35535 6.50857H2.64186C2.08958 6.50857 1.64186 6.06085 1.64186 5.50857C1.64186 4.95628 2.08958 4.50857 2.64186 4.50857H4.56879L4.84815 1.89085C4.90676 1.34169 5.39946 0.94401 5.94862 1.00262C6.49779 1.06122 6.89547 1.55392 6.83686 2.10309L6.58015 4.50857H10.0131L10.2925 1.89085C10.3511 1.34169 10.8438 0.94401 11.393 1.00262Z" fill="black"/>
|
||||||
<path d="M8 11H14" stroke="#737D8C" stroke-width="2" stroke-linecap="round"/>
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.4108 7.28091C11.4108 6.72863 10.963 6.28091 10.4108 6.28091C9.85847 6.28091 9.41076 6.72863 9.41076 7.28091L9.41076 9.44016H7.28076C6.72848 9.44016 6.28076 9.88787 6.28076 10.4402C6.28076 10.9924 6.72848 11.4402 7.28076 11.4402H9.41076L9.41076 13.5994C9.41076 14.1517 9.85847 14.5994 10.4108 14.5994C10.963 14.5994 11.4108 14.1517 11.4108 13.5994V11.4402H13.539C14.0913 11.4402 14.539 10.9924 14.539 10.4402C14.539 9.88787 14.0913 9.44016 13.539 9.44016H11.4108V7.28091Z" fill="black"/>
|
||||||
<path d="M11 8L11 14" stroke="#737D8C" stroke-width="2" stroke-linecap="round"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.6 KiB |
9
res/img/location/map.svg
Normal file
After Width: | Height: | Size: 11 KiB |
153
res/img/matrix.svg
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="793.322px" height="340.809px" viewBox="0 0 793.322 340.809" enable-background="new 0 0 793.322 340.809"
|
||||||
|
xml:space="preserve">
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M34.004,340.809H2c-1.104,0-2-0.896-2-2V2c0-1.104,0.896-2,2-2h32.004c1.104,0,2,0.896,2,2
|
||||||
|
v7.71c0,1.104-0.896,2-2,2h-21.13v317.386h21.13c1.104,0,2,0.896,2,2.001v7.712C36.004,339.913,35.108,340.809,34.004,340.809
|
||||||
|
L34.004,340.809z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M10.875,9.711v321.386h23.13v7.711H1.999V2.001h32.006v7.71H10.875z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M252.402,233.711h-32.993c-1.104,0-2-0.896-2-2v-68.073c0-3.949-0.154-7.722-0.457-11.213
|
||||||
|
c-0.289-3.282-1.074-6.153-2.332-8.53c-1.204-2.276-3.017-4.119-5.384-5.476c-2.393-1.362-5.775-2.056-10.042-2.056
|
||||||
|
c-4.238,0-7.674,0.798-10.213,2.371c-2.565,1.596-4.604,3.701-6.053,6.258c-1.498,2.643-2.51,5.694-3.013,9.067
|
||||||
|
c-0.526,3.513-0.793,7.125-0.793,10.741v66.91c0,1.104-0.896,2-2,2h-32.991c-1.104,0-2-0.896-2-2v-67.373
|
||||||
|
c0-3.435-0.078-6.964-0.228-10.485c-0.148-3.251-0.767-6.278-1.841-8.995c-1.018-2.571-2.667-4.584-5.047-6.153
|
||||||
|
c-2.372-1.552-6.029-2.341-10.865-2.341c-1.372,0-3.265,0.328-5.629,0.976c-2.28,0.624-4.536,1.826-6.705,3.577
|
||||||
|
c-2.152,1.732-4.036,4.306-5.605,7.655c-1.569,3.356-2.367,7.877-2.367,13.438v69.701c0,1.104-0.895,2-2,2H68.857
|
||||||
|
c-1.104,0-2-0.896-2-2V111.594c0-1.104,0.896-1.999,2-1.999h31.13c1.104,0,2,0.896,2,1.999v11.007
|
||||||
|
c3.834-4.499,8.248-8.152,13.173-10.896c6.396-3.559,13.799-5.362,22.002-5.362c7.846,0,15.127,1.548,21.642,4.604
|
||||||
|
c5.794,2.722,10.424,7.26,13.791,13.52c3.449-4.362,7.833-8.306,13.071-11.752c6.422-4.228,14.102-6.371,22.824-6.371
|
||||||
|
c6.499,0,12.625,0.807,18.209,2.399c5.686,1.628,10.635,4.271,14.712,7.857c4.088,3.605,7.318,8.357,9.601,14.123
|
||||||
|
c2.25,5.719,3.391,12.649,3.391,20.604v80.384C254.402,232.815,253.507,233.711,252.402,233.711L252.402,233.711z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M99.988,111.595v16.264h0.463c4.338-6.191,9.563-10.998,15.684-14.406
|
||||||
|
c6.117-3.402,13.129-5.11,21.027-5.11c7.588,0,14.521,1.475,20.793,4.415c6.274,2.945,11.038,8.131,14.291,15.567
|
||||||
|
c3.56-5.265,8.4-9.913,14.521-13.94c6.117-4.025,13.358-6.042,21.724-6.042c6.351,0,12.234,0.776,17.66,2.325
|
||||||
|
c5.418,1.549,10.065,4.027,13.938,7.434c3.869,3.41,6.889,7.863,9.062,13.357c2.167,5.504,3.253,12.122,3.253,19.869v80.385H219.41
|
||||||
|
v-68.074c0-4.025-0.154-7.82-0.465-11.385c-0.313-3.56-1.161-6.656-2.555-9.293c-1.395-2.631-3.45-4.724-6.157-6.274
|
||||||
|
c-2.711-1.543-6.391-2.322-11.037-2.322s-8.403,0.896-11.269,2.671c-2.868,1.784-5.112,4.109-6.737,6.971
|
||||||
|
c-1.626,2.869-2.711,6.12-3.252,9.762c-0.545,3.638-0.814,7.318-0.814,11.035v66.91h-32.991v-67.375c0-3.562-0.081-7.087-0.23-10.57
|
||||||
|
c-0.158-3.487-0.814-6.7-1.978-9.645c-1.162-2.94-3.099-5.304-5.809-7.088c-2.711-1.775-6.699-2.671-11.965-2.671
|
||||||
|
c-1.551,0-3.603,0.349-6.156,1.048c-2.556,0.697-5.036,2.016-7.435,3.949c-2.404,1.938-4.454,4.726-6.158,8.363
|
||||||
|
c-1.705,3.642-2.556,8.402-2.556,14.287v69.701h-32.99V111.595H99.988z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M304.909,236.733c-5.883,0-11.46-0.729-16.574-2.163c-5.192-1.464-9.806-3.774-13.713-6.871
|
||||||
|
c-3.944-3.117-7.068-7.111-9.282-11.871c-2.205-4.733-3.324-10.412-3.324-16.876c0-7.13,1.293-13.117,3.846-17.797
|
||||||
|
c2.542-4.674,5.877-8.464,9.912-11.263c3.97-2.752,8.556-4.842,13.63-6.209c4.901-1.322,9.937-2.394,14.961-3.184
|
||||||
|
c4.986-0.775,9.949-1.404,14.754-1.872c4.679-0.452,8.88-1.139,12.489-2.039c3.412-0.854,6.118-2.09,8.042-3.672
|
||||||
|
c1.666-1.37,2.416-3.384,2.292-6.151c-0.002-3.289-0.502-5.816-1.492-7.595c-0.998-1.798-2.283-3.15-3.927-4.138
|
||||||
|
c-1.703-1.02-3.725-1.713-6.012-2.062c-2.47-0.37-5.146-0.557-7.947-0.557c-6.034,0-10.789,1.271-14.135,3.783
|
||||||
|
c-3.233,2.424-5.155,6.64-5.714,12.527c-0.098,1.026-0.961,1.812-1.992,1.812h-32.992c-0.552,0-1.079-0.229-1.457-0.629
|
||||||
|
c-0.376-0.402-0.572-0.941-0.54-1.491c0.485-8.073,2.55-14.894,6.142-20.272c3.548-5.331,8.147-9.682,13.661-12.931
|
||||||
|
c5.424-3.191,11.612-5.498,18.392-6.857c6.684-1.335,13.5-2.013,20.26-2.013c6.096,0,12.365,0.437,18.626,1.296
|
||||||
|
c6.377,0.88,12.285,2.622,17.562,5.177c5.376,2.604,9.845,6.29,13.282,10.951c3.498,4.744,5.271,11.048,5.271,18.731v62.494
|
||||||
|
c0,5.307,0.306,10.462,0.915,15.319c0.576,4.64,1.572,8.116,2.963,10.338c0.385,0.616,0.407,1.395,0.055,2.031
|
||||||
|
c-0.353,0.635-1.022,1.03-1.75,1.03h-33.457c-0.861,0-1.624-0.55-1.898-1.367c-0.646-1.941-1.176-3.939-1.572-5.936
|
||||||
|
c-0.141-0.696-0.267-1.402-0.38-2.12c-4.825,4.184-10.349,7.24-16.474,9.105C320.033,235.609,312.489,236.733,304.909,236.733
|
||||||
|
L304.909,236.733z M341.941,176.661c-0.809,0.409-1.676,0.768-2.596,1.074c-2.161,0.72-4.511,1.326-6.988,1.807
|
||||||
|
c-2.442,0.475-5.033,0.872-7.699,1.186c-2.631,0.311-5.251,0.697-7.784,1.146c-2.329,0.433-4.705,1.035-7.051,1.792
|
||||||
|
c-2.194,0.711-4.114,1.667-5.699,2.842c-1.531,1.128-2.785,2.587-3.731,4.335c-0.917,1.709-1.385,3.97-1.385,6.719
|
||||||
|
c0,2.598,0.465,4.778,1.385,6.481c0.928,1.722,2.142,3.035,3.716,4.018c1.644,1.026,3.601,1.757,5.816,2.17
|
||||||
|
c2.344,0.439,4.799,0.663,7.297,0.663c6.105,0,10.836-0.996,14.063-2.961c3.244-1.973,5.666-4.349,7.199-7.062
|
||||||
|
c1.568-2.78,2.542-5.62,2.892-8.436c0.376-3.019,0.565-5.436,0.565-7.187V176.661L341.941,176.661z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M273.544,129.255c3.405-5.113,7.744-9.215,13.012-12.316
|
||||||
|
c5.264-3.097,11.186-5.303,17.771-6.621c6.582-1.315,13.205-1.976,19.865-1.976c6.042,0,12.158,0.428,18.354,1.277
|
||||||
|
c6.195,0.855,11.85,2.522,16.962,4.997c5.111,2.477,9.292,5.926,12.546,10.338c3.253,4.414,4.879,10.262,4.879,17.543v62.494
|
||||||
|
c0,5.428,0.31,10.611,0.931,15.567c0.615,4.959,1.701,8.676,3.251,11.153H347.66c-0.621-1.86-1.126-3.755-1.511-5.693
|
||||||
|
c-0.39-1.933-0.661-3.908-0.813-5.923c-5.267,5.422-11.465,9.217-18.585,11.386c-7.127,2.163-14.407,3.251-21.842,3.251
|
||||||
|
c-5.733,0-11.077-0.698-16.033-2.09c-4.958-1.395-9.293-3.562-13.01-6.51c-3.718-2.938-6.622-6.656-8.713-11.147
|
||||||
|
s-3.138-9.84-3.138-16.033c0-6.813,1.199-12.43,3.604-16.84c2.399-4.417,5.495-7.939,9.295-10.575
|
||||||
|
c3.793-2.632,8.129-4.607,13.01-5.923c4.878-1.315,9.795-2.358,14.752-3.137c4.957-0.772,9.835-1.393,14.638-1.857
|
||||||
|
c4.801-0.466,9.062-1.164,12.779-2.093c3.718-0.929,6.658-2.282,8.829-4.065c2.165-1.781,3.172-4.375,3.02-7.785
|
||||||
|
c0-3.56-0.58-6.389-1.742-8.479c-1.161-2.09-2.711-3.719-4.646-4.88c-1.937-1.161-4.183-1.936-6.737-2.325
|
||||||
|
c-2.557-0.382-5.309-0.58-8.248-0.58c-6.506,0-11.617,1.395-15.335,4.183c-3.716,2.788-5.889,7.437-6.506,13.94h-32.991
|
||||||
|
C268.199,140.794,270.132,134.363,273.544,129.255z M338.713,175.838c-2.09,0.696-4.337,1.275-6.736,1.741
|
||||||
|
c-2.402,0.465-4.918,0.853-7.551,1.161c-2.635,0.313-5.268,0.698-7.899,1.163c-2.48,0.461-4.919,1.086-7.317,1.857
|
||||||
|
c-2.404,0.779-4.495,1.822-6.274,3.138c-1.784,1.317-3.216,2.985-4.3,4.994c-1.085,2.014-1.626,4.571-1.626,7.668
|
||||||
|
c0,2.94,0.541,5.422,1.626,7.431c1.084,2.017,2.558,3.604,4.416,4.765s4.025,1.976,6.507,2.438c2.475,0.466,5.031,0.698,7.665,0.698
|
||||||
|
c6.505,0,11.537-1.082,15.103-3.253c3.561-2.166,6.192-4.762,7.899-7.785c1.702-3.019,2.749-6.072,3.137-9.174
|
||||||
|
c0.384-3.097,0.58-5.576,0.58-7.434v-12.316C342.547,174.173,340.805,175.14,338.713,175.838z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M444.542,234.874c-5.187,0-10.173-0.361-14.823-1.069c-4.802-0.732-9.104-2.183-12.779-4.313
|
||||||
|
c-3.789-2.185-6.821-5.341-9.006-9.375c-2.163-3.986-3.26-9.232-3.26-15.59v-68.859h-17.981c-1.104,0-2-0.896-2-1.999v-22.073
|
||||||
|
c0-1.104,0.896-1.999,2-1.999h17.981V75.582c0-1.104,0.896-2,2-2h32.992c1.104,0,2,0.896,2,2v34.014h22.162c1.104,0,2,0.896,2,1.999
|
||||||
|
v22.073c0,1.104-0.896,1.999-2,1.999h-22.162v57.479c0,6.229,1.198,8.731,2.202,9.733c1.004,1.007,3.506,2.205,9.738,2.205
|
||||||
|
c1.804,0,3.542-0.076,5.161-0.225c1.604-0.144,3.174-0.367,4.669-0.665c0.13-0.026,0.261-0.039,0.391-0.039
|
||||||
|
c0.458,0,0.907,0.159,1.27,0.454c0.463,0.379,0.73,0.946,0.73,1.546v25.555c0,0.979-0.707,1.813-1.672,1.974
|
||||||
|
c-2.834,0.472-6.041,0.794-9.527,0.957C451.015,234.798,447.718,234.874,444.542,234.874L444.542,234.874z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M463.825,111.595v22.072h-24.161v59.479c0,5.573,0.928,9.292,2.788,11.149
|
||||||
|
c1.856,1.859,5.576,2.788,11.152,2.788c1.859,0,3.638-0.076,5.343-0.232c1.703-0.152,3.33-0.388,4.878-0.696v25.557
|
||||||
|
c-2.788,0.465-5.887,0.773-9.293,0.931c-3.407,0.149-6.737,0.23-9.99,0.23c-5.111,0-9.953-0.35-14.521-1.048
|
||||||
|
c-4.571-0.695-8.597-2.047-12.081-4.063c-3.486-2.011-6.236-4.88-8.248-8.597c-2.016-3.714-3.021-8.595-3.021-14.639v-70.859h-19.98
|
||||||
|
v-22.072h19.98V75.583h32.992v36.012H463.825z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M512.613,233.711h-32.991c-1.104,0-2-0.896-2-2V111.594c0-1.104,0.896-1.999,2-1.999h31.366
|
||||||
|
c1.104,0,2,0.896,2,1.999v15.069c0.967-1.516,2.034-2.978,3.199-4.382c2.754-3.312,5.949-6.182,9.496-8.522
|
||||||
|
c3.545-2.332,7.385-4.169,11.415-5.462c4.056-1.298,8.327-1.954,12.691-1.954c2.341,0,4.953,0.418,7.766,1.243
|
||||||
|
c0.852,0.25,1.437,1.032,1.437,1.92v30.67c0,0.6-0.269,1.167-0.732,1.547c-0.361,0.296-0.808,0.452-1.265,0.452
|
||||||
|
c-0.133,0-0.265-0.013-0.398-0.039c-1.484-0.3-3.299-0.565-5.392-0.787c-2.098-0.224-4.136-0.339-6.062-0.339
|
||||||
|
c-5.706,0-10.572,0.95-14.467,2.823c-3.862,1.86-7.012,4.428-9.361,7.629c-2.389,3.263-4.115,7.12-5.127,11.47
|
||||||
|
c-1.043,4.479-1.574,9.409-1.574,14.647v54.132C514.613,232.815,513.717,233.711,512.613,233.711L512.613,233.711z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M510.988,111.595V133.9h0.465c1.546-3.72,3.636-7.163,6.272-10.341
|
||||||
|
c2.634-3.172,5.652-5.885,9.06-8.131c3.405-2.242,7.047-3.985,10.923-5.228c3.868-1.237,7.898-1.859,12.081-1.859
|
||||||
|
c2.168,0,4.566,0.39,7.202,1.163v30.67c-1.551-0.312-3.41-0.584-5.576-0.814c-2.17-0.233-4.26-0.35-6.274-0.35
|
||||||
|
c-6.041,0-11.152,1.01-15.332,3.021c-4.182,2.014-7.55,4.761-10.107,8.247c-2.555,3.487-4.379,7.55-5.462,12.198
|
||||||
|
c-1.083,4.645-1.625,9.682-1.625,15.102v54.133h-32.991V111.595H510.988z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M603.923,233.711H570.93c-1.104,0-2-0.896-2-2V111.594c0-1.104,0.896-1.999,2-1.999h32.994
|
||||||
|
c1.104,0,2,0.896,2,1.999v120.117C605.923,232.815,605.027,233.711,603.923,233.711L603.923,233.711z M603.923,95.006H570.93
|
||||||
|
c-1.104,0-2-0.896-2-1.999V65.825c0-1.104,0.896-2,2-2h32.994c1.104,0,2,0.896,2,2v27.182
|
||||||
|
C605.923,94.11,605.027,95.006,603.923,95.006L603.923,95.006z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M570.93,93.007V65.824h32.994v27.183H570.93z M603.924,111.595v120.117H570.93V111.595
|
||||||
|
H603.924z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M742.163,233.711h-37.64c-0.671,0-1.297-0.335-1.667-0.896l-23.426-35.352l-23.426,35.352
|
||||||
|
c-0.369,0.561-0.995,0.896-1.667,0.896h-36.938c-0.741,0-1.424-0.411-1.77-1.067c-0.345-0.654-0.3-1.449,0.118-2.061l42.435-62.055
|
||||||
|
l-38.71-55.793c-0.424-0.613-0.474-1.408-0.128-2.069c0.343-0.658,1.028-1.071,1.771-1.071h37.636c0.665,0,1.287,0.33,1.658,0.882
|
||||||
|
l19.477,28.893l19.255-28.884c0.372-0.556,0.996-0.891,1.665-0.891h36.475c0.746,0,1.43,0.415,1.776,1.078
|
||||||
|
c0.343,0.66,0.289,1.46-0.139,2.071l-38.69,55.082l43.578,62.744c0.424,0.61,0.474,1.408,0.128,2.066
|
||||||
|
C743.591,233.298,742.908,233.711,742.163,233.711L742.163,233.711z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M621.115,111.595h37.637l21.144,31.365l20.911-31.365h36.476l-39.496,56.226l44.377,63.892
|
||||||
|
h-37.64l-25.093-37.87l-25.094,37.87h-36.938l43.213-63.193L621.115,111.595z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M791.322,340.809h-32.008c-1.105,0-2-0.896-2-2v-7.712c0-1.105,0.896-2.001,2-2.001h21.13
|
||||||
|
V11.71h-21.13c-1.105,0-2-0.896-2-2V2c0-1.104,0.896-2,2-2h32.008c1.104,0,2,0.896,2,2v336.809
|
||||||
|
C793.322,339.913,792.426,340.809,791.322,340.809L791.322,340.809z"/>
|
||||||
|
<path opacity="0.5" fill="#FFFFFF" d="M782.443,331.097V9.711h-23.13v-7.71h32.008v336.807h-32.008v-7.711H782.443z"/>
|
||||||
|
<path d="M10.875,9.711v321.386h23.13v7.711H1.999V2.001h32.006v7.71H10.875z"/>
|
||||||
|
<path d="M99.988,111.595v16.264h0.463c4.338-6.191,9.563-10.998,15.684-14.406c6.117-3.402,13.129-5.11,21.027-5.11
|
||||||
|
c7.588,0,14.521,1.475,20.793,4.415c6.274,2.945,11.038,8.131,14.291,15.567c3.56-5.265,8.4-9.913,14.521-13.94
|
||||||
|
c6.117-4.025,13.358-6.042,21.724-6.042c6.351,0,12.234,0.776,17.66,2.325c5.418,1.549,10.065,4.027,13.938,7.434
|
||||||
|
c3.869,3.41,6.889,7.863,9.062,13.357c2.167,5.504,3.253,12.122,3.253,19.869v80.385H219.41v-68.074
|
||||||
|
c0-4.025-0.154-7.82-0.465-11.385c-0.313-3.56-1.161-6.656-2.555-9.293c-1.395-2.631-3.45-4.724-6.157-6.274
|
||||||
|
c-2.711-1.543-6.391-2.322-11.037-2.322s-8.403,0.896-11.269,2.671c-2.868,1.784-5.112,4.109-6.737,6.971
|
||||||
|
c-1.626,2.869-2.711,6.12-3.252,9.762c-0.545,3.638-0.814,7.318-0.814,11.035v66.91h-32.991v-67.375c0-3.562-0.081-7.087-0.23-10.57
|
||||||
|
c-0.158-3.487-0.814-6.7-1.978-9.645c-1.162-2.94-3.099-5.304-5.809-7.088c-2.711-1.775-6.699-2.671-11.965-2.671
|
||||||
|
c-1.551,0-3.603,0.349-6.156,1.048c-2.556,0.697-5.036,2.016-7.435,3.949c-2.404,1.938-4.454,4.726-6.158,8.363
|
||||||
|
c-1.705,3.642-2.556,8.402-2.556,14.287v69.701h-32.99V111.595H99.988z"/>
|
||||||
|
<path d="M273.544,129.255c3.405-5.113,7.744-9.215,13.012-12.316c5.264-3.097,11.186-5.303,17.771-6.621
|
||||||
|
c6.582-1.315,13.205-1.976,19.865-1.976c6.042,0,12.158,0.428,18.354,1.277c6.195,0.855,11.85,2.522,16.962,4.997
|
||||||
|
c5.111,2.477,9.292,5.926,12.546,10.338c3.253,4.414,4.879,10.262,4.879,17.543v62.494c0,5.428,0.31,10.611,0.931,15.567
|
||||||
|
c0.615,4.959,1.701,8.676,3.251,11.153H347.66c-0.621-1.86-1.126-3.755-1.511-5.693c-0.39-1.933-0.661-3.908-0.813-5.923
|
||||||
|
c-5.267,5.422-11.465,9.217-18.585,11.386c-7.127,2.163-14.407,3.251-21.842,3.251c-5.733,0-11.077-0.698-16.033-2.09
|
||||||
|
c-4.958-1.395-9.293-3.562-13.01-6.51c-3.718-2.938-6.622-6.656-8.713-11.147s-3.138-9.84-3.138-16.033
|
||||||
|
c0-6.813,1.199-12.43,3.604-16.84c2.399-4.417,5.495-7.939,9.295-10.575c3.793-2.632,8.129-4.607,13.01-5.923
|
||||||
|
c4.878-1.315,9.795-2.358,14.752-3.137c4.957-0.772,9.835-1.393,14.638-1.857c4.801-0.466,9.062-1.164,12.779-2.093
|
||||||
|
c3.718-0.929,6.658-2.282,8.829-4.065c2.165-1.781,3.172-4.375,3.02-7.785c0-3.56-0.58-6.389-1.742-8.479
|
||||||
|
c-1.161-2.09-2.711-3.719-4.646-4.88c-1.937-1.161-4.183-1.936-6.737-2.325c-2.557-0.382-5.309-0.58-8.248-0.58
|
||||||
|
c-6.506,0-11.617,1.395-15.335,4.183c-3.716,2.788-5.889,7.437-6.506,13.94h-32.991
|
||||||
|
C268.199,140.794,270.132,134.363,273.544,129.255z M338.713,175.838c-2.09,0.696-4.337,1.275-6.736,1.741
|
||||||
|
c-2.402,0.465-4.918,0.853-7.551,1.161c-2.635,0.313-5.268,0.698-7.899,1.163c-2.48,0.461-4.919,1.086-7.317,1.857
|
||||||
|
c-2.404,0.779-4.495,1.822-6.274,3.138c-1.784,1.317-3.216,2.985-4.3,4.994c-1.085,2.014-1.626,4.571-1.626,7.668
|
||||||
|
c0,2.94,0.541,5.422,1.626,7.431c1.084,2.017,2.558,3.604,4.416,4.765s4.025,1.976,6.507,2.438c2.475,0.466,5.031,0.698,7.665,0.698
|
||||||
|
c6.505,0,11.537-1.082,15.103-3.253c3.561-2.166,6.192-4.762,7.899-7.785c1.702-3.019,2.749-6.072,3.137-9.174
|
||||||
|
c0.384-3.097,0.58-5.576,0.58-7.434v-12.316C342.547,174.173,340.805,175.14,338.713,175.838z"/>
|
||||||
|
<path d="M463.825,111.595v22.072h-24.161v59.479c0,5.573,0.928,9.292,2.788,11.149c1.856,1.859,5.576,2.788,11.152,2.788
|
||||||
|
c1.859,0,3.638-0.076,5.343-0.232c1.703-0.152,3.33-0.388,4.878-0.696v25.557c-2.788,0.465-5.887,0.773-9.293,0.931
|
||||||
|
c-3.407,0.149-6.737,0.23-9.99,0.23c-5.111,0-9.953-0.35-14.521-1.048c-4.571-0.695-8.597-2.047-12.081-4.063
|
||||||
|
c-3.486-2.011-6.236-4.88-8.248-8.597c-2.016-3.714-3.021-8.595-3.021-14.639v-70.859h-19.98v-22.072h19.98V75.583h32.992v36.012
|
||||||
|
H463.825z"/>
|
||||||
|
<path d="M510.988,111.595V133.9h0.465c1.546-3.72,3.636-7.163,6.272-10.341c2.634-3.172,5.652-5.885,9.06-8.131
|
||||||
|
c3.405-2.242,7.047-3.985,10.923-5.228c3.868-1.237,7.898-1.859,12.081-1.859c2.168,0,4.566,0.39,7.202,1.163v30.67
|
||||||
|
c-1.551-0.312-3.41-0.584-5.576-0.814c-2.17-0.233-4.26-0.35-6.274-0.35c-6.041,0-11.152,1.01-15.332,3.021
|
||||||
|
c-4.182,2.014-7.55,4.761-10.107,8.247c-2.555,3.487-4.379,7.55-5.462,12.198c-1.083,4.645-1.625,9.682-1.625,15.102v54.133h-32.991
|
||||||
|
V111.595H510.988z"/>
|
||||||
|
<path d="M570.93,93.007V65.824h32.994v27.183H570.93z M603.924,111.595v120.117H570.93V111.595H603.924z"/>
|
||||||
|
<path d="M621.115,111.595h37.637l21.144,31.365l20.911-31.365h36.476l-39.496,56.226l44.377,63.892h-37.64l-25.093-37.87
|
||||||
|
l-25.094,37.87h-36.938l43.213-63.193L621.115,111.595z"/>
|
||||||
|
<path d="M782.443,331.097V9.711h-23.13v-7.71h32.008v336.807h-32.008v-7.711H782.443z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 16 KiB |
|
@ -7,7 +7,9 @@ $quaternary-content: #6F7882;
|
||||||
$quinary-content: #394049;
|
$quinary-content: #394049;
|
||||||
|
|
||||||
$system: #21262C;
|
$system: #21262C;
|
||||||
|
$system-transparent: rgba($system, 0.0); // XXX: workaround for Safari 15.3 linear-gradient bug
|
||||||
$background: #15191E;
|
$background: #15191E;
|
||||||
|
$overlay-background: rgba($background, 0.85);
|
||||||
|
|
||||||
$panel-base: #8D97A5; // This color is not intended for use in the app
|
$panel-base: #8D97A5; // This color is not intended for use in the app
|
||||||
$panels: rgba($system, 0.9);
|
$panels: rgba($system, 0.9);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// Colors from Figma Compound https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=559%3A741
|
// Colors from Figma Compound https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=559%3A741
|
||||||
$system: #21262C;
|
$system: #21262C;
|
||||||
|
$system-transparent: rgba($system, 0.0); // XXX: workaround for Safari 15.3 linear-gradient bug
|
||||||
|
|
||||||
// unified palette
|
// unified palette
|
||||||
// try to use these colors when possible
|
// try to use these colors when possible
|
||||||
|
@ -97,7 +98,10 @@ $tertiary-content: $tertiary-fg-color;
|
||||||
$quaternary-content: #6F7882;
|
$quaternary-content: #6F7882;
|
||||||
$quinary-content: $quaternary-content;
|
$quinary-content: $quaternary-content;
|
||||||
$system: #21262C;
|
$system: #21262C;
|
||||||
|
$system-transparent: rgba($system, 0.0); // XXX: workaround for Safari 15.3 linear-gradient bug
|
||||||
$background: $primary-bg-color;
|
$background: $primary-bg-color;
|
||||||
|
$overlay-background: rgba($background, 0.85);
|
||||||
|
|
||||||
$panels: rgba($system, 0.9);
|
$panels: rgba($system, 0.9);
|
||||||
$panel-base: #8D97A5; // This color is not intended for use in the app
|
$panel-base: #8D97A5; // This color is not intended for use in the app
|
||||||
$panel-selected: rgba($panel-base, 0.3);
|
$panel-selected: rgba($panel-base, 0.3);
|
||||||
|
|
|
@ -150,7 +150,10 @@ $tertiary-content: $tertiary-fg-color;
|
||||||
$quaternary-content: #6F7882;
|
$quaternary-content: #6F7882;
|
||||||
$quinary-content: $quaternary-content;
|
$quinary-content: $quaternary-content;
|
||||||
$system: #F4F6FA;
|
$system: #F4F6FA;
|
||||||
|
$system-transparent: rgba($system, 0.0); // XXX: workaround for Safari 15.3 linear-gradient bug
|
||||||
$background: $primary-bg-color;
|
$background: $primary-bg-color;
|
||||||
|
$overlay-background: rgba($background, 0.85);
|
||||||
|
|
||||||
$panels: rgba($system, 0.9);
|
$panels: rgba($system, 0.9);
|
||||||
$panel-base: #8D97A5; // This color is not intended for use in the app
|
$panel-base: #8D97A5; // This color is not intended for use in the app
|
||||||
$panel-selected: rgba($tertiary-content, 0.3);
|
$panel-selected: rgba($tertiary-content, 0.3);
|
||||||
|
|
|
@ -26,6 +26,8 @@ $secondary-content: var(--secondary-content, $secondary-content);
|
||||||
$tertiary-content: var(--tertiary-content, $tertiary-content);
|
$tertiary-content: var(--tertiary-content, $tertiary-content);
|
||||||
$quaternary-content: var(--quaternary-content, $quaternary-content);
|
$quaternary-content: var(--quaternary-content, $quaternary-content);
|
||||||
$quinary-content: var(--quinary-content, $quinary-content);
|
$quinary-content: var(--quinary-content, $quinary-content);
|
||||||
|
// XXX: workaround for Safari 15.3 linear-gradient bug https://github.com/vector-im/element-web/issues/21670
|
||||||
|
$system-transparent: var(--system-transparent, rgba($system, 0.0));
|
||||||
$system: var(--system, $system);
|
$system: var(--system, $system);
|
||||||
$background: var(--background, $background);
|
$background: var(--background, $background);
|
||||||
$panels: rgba($system, 0.9);
|
$panels: rgba($system, 0.9);
|
||||||
|
|
|
@ -34,7 +34,9 @@ $quaternary-content: #c1c6cd;
|
||||||
$quinary-content: #E3E8F0;
|
$quinary-content: #E3E8F0;
|
||||||
|
|
||||||
$system: #F4F6FA;
|
$system: #F4F6FA;
|
||||||
|
$system-transparent: rgba($system, 0.0); // XXX: workaround for Safari 15.3 linear-gradient bug
|
||||||
$background: #ffffff;
|
$background: #ffffff;
|
||||||
|
$overlay-background: rgba($background, 0.85);
|
||||||
|
|
||||||
$panels: rgba($system, 0.9);
|
$panels: rgba($system, 0.9);
|
||||||
$panel-selected: rgba($tertiary-content, 0.3);
|
$panel-selected: rgba($tertiary-content, 0.3);
|
||||||
|
|
|
@ -31,7 +31,6 @@ yarn link matrix-js-sdk
|
||||||
yarn link matrix-analytics-events
|
yarn link matrix-analytics-events
|
||||||
yarn link
|
yarn link
|
||||||
yarn install --pure-lockfile
|
yarn install --pure-lockfile
|
||||||
yarn reskindex
|
|
||||||
|
|
||||||
# Finally, set up element-web
|
# Finally, set up element-web
|
||||||
scripts/fetchdep.sh vector-im element-web
|
scripts/fetchdep.sh vector-im element-web
|
||||||
|
|
|
@ -57,7 +57,10 @@ BRANCH_ARRAY=(${head//:/ })
|
||||||
TRY_ORG=$deforg
|
TRY_ORG=$deforg
|
||||||
TRY_BRANCH=${BRANCH_ARRAY[0]}
|
TRY_BRANCH=${BRANCH_ARRAY[0]}
|
||||||
if [[ "$head" == *":"* ]]; then
|
if [[ "$head" == *":"* ]]; then
|
||||||
TRY_ORG=${BRANCH_ARRAY[0]}
|
# ... but only match that fork if it's a real fork
|
||||||
|
if [ "${BRANCH_ARRAY[0]}" != "matrix-org" ]; then
|
||||||
|
TRY_ORG=${BRANCH_ARRAY[0]}
|
||||||
|
fi
|
||||||
TRY_BRANCH=${BRANCH_ARRAY[1]}
|
TRY_BRANCH=${BRANCH_ARRAY[1]}
|
||||||
fi
|
fi
|
||||||
clone ${TRY_ORG} $defrepo ${TRY_BRANCH}
|
clone ${TRY_ORG} $defrepo ${TRY_BRANCH}
|
||||||
|
|
|
@ -34,7 +34,6 @@ export default %%ComponentName%%;
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { mount } from 'enzyme';
|
import { mount } from 'enzyme';
|
||||||
|
|
||||||
import '%%SkinnedSdkPath%%';
|
|
||||||
import %%ComponentName%% from '%%RelativeComponentPath%%';
|
import %%ComponentName%% from '%%RelativeComponentPath%%';
|
||||||
|
|
||||||
describe('<%%ComponentName%% />', () => {
|
describe('<%%ComponentName%% />', () => {
|
||||||
|
@ -85,10 +84,8 @@ const makeFile = async ({
|
||||||
const relativePathToComponent = path.parse(path.relative(path.dirname(newFilePath), componentFilePath || ''));
|
const relativePathToComponent = path.parse(path.relative(path.dirname(newFilePath), componentFilePath || ''));
|
||||||
const importComponentPath = path.join(relativePathToComponent.dir, relativePathToComponent.name);
|
const importComponentPath = path.join(relativePathToComponent.dir, relativePathToComponent.name);
|
||||||
|
|
||||||
const skinnedSdkPath = path.relative(path.dirname(newFilePath), 'test/skinned-sdk')
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(newFilePath, fillTemplate(template, componentName, importComponentPath, skinnedSdkPath), { flag: 'wx' });
|
await fs.writeFile(newFilePath, fillTemplate(template, componentName, importComponentPath), { flag: 'wx' });
|
||||||
console.log(`Created ${path.relative(process.cwd(), newFilePath)}`);
|
console.log(`Created ${path.relative(process.cwd(), newFilePath)}`);
|
||||||
return newFilePath;
|
return newFilePath;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -104,7 +101,6 @@ const makeFile = async ({
|
||||||
const fillTemplate = (template, componentName, relativeComponentFilePath, skinnedSdkPath) =>
|
const fillTemplate = (template, componentName, relativeComponentFilePath, skinnedSdkPath) =>
|
||||||
template.replace(/%%ComponentName%%/g, componentName)
|
template.replace(/%%ComponentName%%/g, componentName)
|
||||||
.replace(/%%RelativeComponentPath%%/g, relativeComponentFilePath)
|
.replace(/%%RelativeComponentPath%%/g, relativeComponentFilePath)
|
||||||
.replace(/%%SkinnedSdkPath%%/g, skinnedSdkPath)
|
|
||||||
|
|
||||||
|
|
||||||
const makeReactComponent = async () => {
|
const makeReactComponent = async () => {
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
const fs = require('fs');
|
|
||||||
const { promises: fsp } = fs;
|
|
||||||
const path = require('path');
|
|
||||||
const glob = require('glob');
|
|
||||||
const util = require('util');
|
|
||||||
const args = require('minimist')(process.argv);
|
|
||||||
const chokidar = require('chokidar');
|
|
||||||
|
|
||||||
const componentIndex = path.join('src', 'component-index.js');
|
|
||||||
const componentIndexTmp = componentIndex+".tmp";
|
|
||||||
const componentsDir = path.join('src', 'components');
|
|
||||||
const componentJsGlob = '**/*.js';
|
|
||||||
const componentTsGlob = '**/*.tsx';
|
|
||||||
let prevFiles = [];
|
|
||||||
|
|
||||||
async function reskindex() {
|
|
||||||
const jsFiles = glob.sync(componentJsGlob, {cwd: componentsDir}).sort();
|
|
||||||
const tsFiles = glob.sync(componentTsGlob, {cwd: componentsDir}).sort();
|
|
||||||
const files = [...tsFiles, ...jsFiles];
|
|
||||||
if (!filesHaveChanged(files, prevFiles)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prevFiles = files;
|
|
||||||
|
|
||||||
const header = args.h || args.header;
|
|
||||||
|
|
||||||
const strm = fs.createWriteStream(componentIndexTmp);
|
|
||||||
// Wait for the open event to ensure the file descriptor is set
|
|
||||||
await new Promise(resolve => strm.once("open", resolve));
|
|
||||||
|
|
||||||
if (header) {
|
|
||||||
strm.write(fs.readFileSync(header));
|
|
||||||
strm.write('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
strm.write("/*\n");
|
|
||||||
strm.write(" * THIS FILE IS AUTO-GENERATED\n");
|
|
||||||
strm.write(" * You can edit it you like, but your changes will be overwritten,\n");
|
|
||||||
strm.write(" * so you'd just be trying to swim upstream like a salmon.\n");
|
|
||||||
strm.write(" * You are not a salmon.\n");
|
|
||||||
strm.write(" */\n\n");
|
|
||||||
strm.write("let components = {};\n");
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; ++i) {
|
|
||||||
const file = files[i].replace('.js', '').replace('.tsx', '');
|
|
||||||
|
|
||||||
const moduleName = (file.replace(/\//g, '.'));
|
|
||||||
const importName = moduleName.replace(/\./g, "$");
|
|
||||||
|
|
||||||
strm.write("import " + importName + " from './components/" + file + "';\n");
|
|
||||||
strm.write(importName + " && (components['"+moduleName+"'] = " + importName + ");");
|
|
||||||
strm.write('\n');
|
|
||||||
strm.uncork();
|
|
||||||
}
|
|
||||||
|
|
||||||
strm.write("export {components};\n");
|
|
||||||
// Ensure the file has been fully written to disk before proceeding
|
|
||||||
await util.promisify(fs.fsync)(strm.fd);
|
|
||||||
await util.promisify(strm.end);
|
|
||||||
await fsp.rename(componentIndexTmp, componentIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expects both arrays of file names to be sorted
|
|
||||||
function filesHaveChanged(files, prevFiles) {
|
|
||||||
if (files.length !== prevFiles.length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Check for name changes
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
|
||||||
if (prevFiles[i] !== files[i]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper since await at the top level is not well supported yet
|
|
||||||
function run() {
|
|
||||||
(async function() {
|
|
||||||
await reskindex();
|
|
||||||
console.log("Reskindex completed");
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
// -w indicates watch mode where any FS events will trigger reskindex
|
|
||||||
if (!args.w) {
|
|
||||||
run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let watchDebouncer = null;
|
|
||||||
chokidar.watch(path.join(componentsDir, componentJsGlob)).on('all', (event, path) => {
|
|
||||||
if (path === componentIndex) return;
|
|
||||||
if (watchDebouncer) clearTimeout(watchDebouncer);
|
|
||||||
watchDebouncer = setTimeout(run, 1000);
|
|
||||||
});
|
|
|
@ -1,38 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2021 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare module "browser-encrypt-attachment" {
|
|
||||||
interface IInfo {
|
|
||||||
v: string;
|
|
||||||
key: {
|
|
||||||
alg: string;
|
|
||||||
key_ops: string[]; // eslint-disable-line camelcase
|
|
||||||
kty: string;
|
|
||||||
k: string;
|
|
||||||
ext: boolean;
|
|
||||||
};
|
|
||||||
iv: string;
|
|
||||||
hashes: {[alg: string]: string};
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IEncryptedAttachment {
|
|
||||||
data: ArrayBuffer;
|
|
||||||
info: IInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function encryptAttachment(plaintextBuffer: ArrayBuffer): Promise<IEncryptedAttachment>;
|
|
||||||
export function decryptAttachment(ciphertextBuffer: ArrayBuffer, info: IInfo): Promise<ArrayBuffer>;
|
|
||||||
}
|
|
4
src/@types/global.d.ts
vendored
|
@ -29,7 +29,6 @@ import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore";
|
||||||
import { IntegrationManagers } from "../integrations/IntegrationManagers";
|
import { IntegrationManagers } from "../integrations/IntegrationManagers";
|
||||||
import { ModalManager } from "../Modal";
|
import { ModalManager } from "../Modal";
|
||||||
import SettingsStore from "../settings/SettingsStore";
|
import SettingsStore from "../settings/SettingsStore";
|
||||||
import { ActiveRoomObserver } from "../ActiveRoomObserver";
|
|
||||||
import { Notifier } from "../Notifier";
|
import { Notifier } from "../Notifier";
|
||||||
import type { Renderer } from "react-dom";
|
import type { Renderer } from "react-dom";
|
||||||
import RightPanelStore from "../stores/right-panel/RightPanelStore";
|
import RightPanelStore from "../stores/right-panel/RightPanelStore";
|
||||||
|
@ -50,7 +49,6 @@ import { SetupEncryptionStore } from "../stores/SetupEncryptionStore";
|
||||||
import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
|
import { RoomScrollStateStore } from "../stores/RoomScrollStateStore";
|
||||||
import { ConsoleLogger, IndexedDBLogStore } from "../rageshake/rageshake";
|
import { ConsoleLogger, IndexedDBLogStore } from "../rageshake/rageshake";
|
||||||
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
import ActiveWidgetStore from "../stores/ActiveWidgetStore";
|
||||||
import { Skinner } from "../Skinner";
|
|
||||||
import AutoRageshakeStore from "../stores/AutoRageshakeStore";
|
import AutoRageshakeStore from "../stores/AutoRageshakeStore";
|
||||||
import { IConfigOptions } from "../IConfigOptions";
|
import { IConfigOptions } from "../IConfigOptions";
|
||||||
|
|
||||||
|
@ -83,7 +81,6 @@ declare global {
|
||||||
mxDeviceListener: DeviceListener;
|
mxDeviceListener: DeviceListener;
|
||||||
mxRoomListStore: RoomListStoreClass;
|
mxRoomListStore: RoomListStoreClass;
|
||||||
mxRoomListLayoutStore: RoomListLayoutStore;
|
mxRoomListLayoutStore: RoomListLayoutStore;
|
||||||
mxActiveRoomObserver: ActiveRoomObserver;
|
|
||||||
mxPlatformPeg: PlatformPeg;
|
mxPlatformPeg: PlatformPeg;
|
||||||
mxIntegrationManagers: typeof IntegrationManagers;
|
mxIntegrationManagers: typeof IntegrationManagers;
|
||||||
singletonModalManager: ModalManager;
|
singletonModalManager: ModalManager;
|
||||||
|
@ -107,7 +104,6 @@ declare global {
|
||||||
mxSetupEncryptionStore?: SetupEncryptionStore;
|
mxSetupEncryptionStore?: SetupEncryptionStore;
|
||||||
mxRoomScrollStateStore?: RoomScrollStateStore;
|
mxRoomScrollStateStore?: RoomScrollStateStore;
|
||||||
mxActiveWidgetStore?: ActiveWidgetStore;
|
mxActiveWidgetStore?: ActiveWidgetStore;
|
||||||
mxSkinner?: Skinner;
|
|
||||||
mxOnRecaptchaLoaded?: () => void;
|
mxOnRecaptchaLoaded?: () => void;
|
||||||
electron?: Electron;
|
electron?: Electron;
|
||||||
mxSendSentryReport: (userText: string, issueUrl: string, error: Error) => Promise<void>;
|
mxSendSentryReport: (userText: string, issueUrl: string, error: Error) => Promise<void>;
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2017 - 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 { logger } from "matrix-js-sdk/src/logger";
|
|
||||||
|
|
||||||
import RoomViewStore from './stores/RoomViewStore';
|
|
||||||
|
|
||||||
type Listener = (isActive: boolean) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consumes changes from the RoomViewStore and notifies specific things
|
|
||||||
* about when the active room changes. Unlike listening for RoomViewStore
|
|
||||||
* changes, you can subscribe to only changes relevant to a particular
|
|
||||||
* room.
|
|
||||||
*
|
|
||||||
* TODO: If we introduce an observer for something else, factor out
|
|
||||||
* the adding / removing of listeners & emitting into a common class.
|
|
||||||
*/
|
|
||||||
export class ActiveRoomObserver {
|
|
||||||
private listeners: {[key: string]: Listener[]} = {};
|
|
||||||
private _activeRoomId = RoomViewStore.getRoomId();
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
// TODO: We could self-destruct when the last listener goes away, or at least stop listening.
|
|
||||||
RoomViewStore.addListener(this.onRoomViewStoreUpdate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get activeRoomId(): string {
|
|
||||||
return this._activeRoomId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public addListener(roomId, listener) {
|
|
||||||
if (!this.listeners[roomId]) this.listeners[roomId] = [];
|
|
||||||
this.listeners[roomId].push(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeListener(roomId, listener) {
|
|
||||||
if (this.listeners[roomId]) {
|
|
||||||
const i = this.listeners[roomId].indexOf(listener);
|
|
||||||
if (i > -1) {
|
|
||||||
this.listeners[roomId].splice(i, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.warn("Unregistering unrecognised listener (roomId=" + roomId + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private emit(roomId, isActive: boolean) {
|
|
||||||
if (!this.listeners[roomId]) return;
|
|
||||||
|
|
||||||
for (const l of this.listeners[roomId]) {
|
|
||||||
l.call(null, isActive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private onRoomViewStoreUpdate = () => {
|
|
||||||
// emit for the old room ID
|
|
||||||
if (this._activeRoomId) this.emit(this._activeRoomId, false);
|
|
||||||
|
|
||||||
// update our cache
|
|
||||||
this._activeRoomId = RoomViewStore.getRoomId();
|
|
||||||
|
|
||||||
// and emit for the new one
|
|
||||||
if (this._activeRoomId) this.emit(this._activeRoomId, true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.mxActiveRoomObserver === undefined) {
|
|
||||||
window.mxActiveRoomObserver = new ActiveRoomObserver();
|
|
||||||
}
|
|
||||||
export default window.mxActiveRoomObserver;
|
|
|
@ -23,7 +23,7 @@ import { getCurrentLanguage, _t, _td, IVariables } from './languageHandler';
|
||||||
import PlatformPeg from './PlatformPeg';
|
import PlatformPeg from './PlatformPeg';
|
||||||
import SdkConfig from './SdkConfig';
|
import SdkConfig from './SdkConfig';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './index';
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
import { SnakedObject } from "./utils/SnakedObject";
|
import { SnakedObject } from "./utils/SnakedObject";
|
||||||
import { IConfigOptions } from "./IConfigOptions";
|
import { IConfigOptions } from "./IConfigOptions";
|
||||||
|
|
||||||
|
@ -406,14 +406,12 @@ export class Analytics {
|
||||||
{ expl: _td('Your device resolution'), value: resolution },
|
{ expl: _td('Your device resolution'), value: resolution },
|
||||||
];
|
];
|
||||||
|
|
||||||
// FIXME: Using an import will result in test failures
|
|
||||||
const piwikConfig = SdkConfig.get("piwik");
|
const piwikConfig = SdkConfig.get("piwik");
|
||||||
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
|
let piwik: Optional<SnakedObject<Extract<IConfigOptions["piwik"], object>>>;
|
||||||
if (typeof piwikConfig === 'object') {
|
if (typeof piwikConfig === 'object') {
|
||||||
piwik = new SnakedObject(piwikConfig);
|
piwik = new SnakedObject(piwikConfig);
|
||||||
}
|
}
|
||||||
const cookiePolicyUrl = piwik?.get("policy_url");
|
const cookiePolicyUrl = piwik?.get("policy_url");
|
||||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
|
||||||
const cookiePolicyLink = _t(
|
const cookiePolicyLink = _t(
|
||||||
"Our complete cookie policy can be found <CookiePolicyLink>here</CookiePolicyLink>.",
|
"Our complete cookie policy can be found <CookiePolicyLink>here</CookiePolicyLink>.",
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -17,9 +17,11 @@ limitations under the License.
|
||||||
import React, { ComponentType } from "react";
|
import React, { ComponentType } from "react";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import * as sdk from './index';
|
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import { IDialogProps } from "./components/views/dialogs/IDialogProps";
|
import { IDialogProps } from "./components/views/dialogs/IDialogProps";
|
||||||
|
import BaseDialog from "./components/views/dialogs/BaseDialog";
|
||||||
|
import DialogButtons from "./components/views/elements/DialogButtons";
|
||||||
|
import Spinner from "./components/views/elements/Spinner";
|
||||||
|
|
||||||
type AsyncImport<T> = { default: T };
|
type AsyncImport<T> = { default: T };
|
||||||
|
|
||||||
|
@ -78,9 +80,6 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
|
||||||
const Component = this.state.component;
|
const Component = this.state.component;
|
||||||
return <Component {...this.props} />;
|
return <Component {...this.props} />;
|
||||||
} else if (this.state.error) {
|
} else if (this.state.error) {
|
||||||
// FIXME: Using an import will result in test failures
|
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
|
||||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
|
||||||
return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}>
|
return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}>
|
||||||
{ _t("Unable to load! Check your network connectivity and try again.") }
|
{ _t("Unable to load! Check your network connectivity and try again.") }
|
||||||
<DialogButtons primaryButton={_t("Dismiss")}
|
<DialogButtons primaryButton={_t("Dismiss")}
|
||||||
|
@ -90,7 +89,6 @@ export default class AsyncWrapper extends React.Component<IProps, IState> {
|
||||||
</BaseDialog>;
|
</BaseDialog>;
|
||||||
} else {
|
} else {
|
||||||
// show a spinner until the component is loaded.
|
// show a spinner until the component is loaded.
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
|
||||||
return <Spinner />;
|
return <Spinner />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ function isValidHexColor(color: string): boolean {
|
||||||
return typeof color === "string" &&
|
return typeof color === "string" &&
|
||||||
(color.length === 7 || color.length === 9) &&
|
(color.length === 7 || color.length === 9) &&
|
||||||
color.charAt(0) === "#" &&
|
color.charAt(0) === "#" &&
|
||||||
!color.substr(1).split("").some(c => isNaN(parseInt(c, 16)));
|
!color.slice(1).split("").some(c => isNaN(parseInt(c, 16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function urlForColor(color: string): string {
|
function urlForColor(color: string): string {
|
||||||
|
|
|
@ -145,6 +145,13 @@ export default abstract class BasePlatform {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if platform allows overriding native context menus
|
||||||
|
*/
|
||||||
|
public allowOverridingNativeContextMenus(): boolean {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the platform supports displaying
|
* Returns true if the platform supports displaying
|
||||||
* notifications, otherwise false.
|
* notifications, otherwise false.
|
||||||
|
|
|
@ -44,7 +44,6 @@ import { WidgetType } from "./widgets/WidgetType";
|
||||||
import { SettingLevel } from "./settings/SettingLevel";
|
import { SettingLevel } from "./settings/SettingLevel";
|
||||||
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
||||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
import InviteDialog, { KIND_CALL_TRANSFER } from "./components/views/dialogs/InviteDialog";
|
|
||||||
import WidgetStore from "./stores/WidgetStore";
|
import WidgetStore from "./stores/WidgetStore";
|
||||||
import { WidgetMessagingStore } from "./stores/widgets/WidgetMessagingStore";
|
import { WidgetMessagingStore } from "./stores/widgets/WidgetMessagingStore";
|
||||||
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
import { ElementWidgetActions } from "./stores/widgets/ElementWidgetActions";
|
||||||
|
@ -54,12 +53,15 @@ import { Action } from './dispatcher/actions';
|
||||||
import VoipUserMapper from './VoipUserMapper';
|
import VoipUserMapper from './VoipUserMapper';
|
||||||
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
|
||||||
import SdkConfig from './SdkConfig';
|
import SdkConfig from './SdkConfig';
|
||||||
import { ensureDMExists, findDMForUser } from './createRoom';
|
import { ensureDMExists } from './createRoom';
|
||||||
import { Container, WidgetLayoutStore } from './stores/widgets/WidgetLayoutStore';
|
import { Container, WidgetLayoutStore } from './stores/widgets/WidgetLayoutStore';
|
||||||
import IncomingCallToast, { getIncomingCallToastKey } from './toasts/IncomingCallToast';
|
import IncomingCallToast, { getIncomingCallToastKey } from './toasts/IncomingCallToast';
|
||||||
import ToastStore from './stores/ToastStore';
|
import ToastStore from './stores/ToastStore';
|
||||||
import Resend from './Resend';
|
import Resend from './Resend';
|
||||||
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
|
import { ViewRoomPayload } from "./dispatcher/payloads/ViewRoomPayload";
|
||||||
|
import { findDMForUser } from "./utils/direct-messages";
|
||||||
|
import { KIND_CALL_TRANSFER } from "./components/views/dialogs/InviteDialogTypes";
|
||||||
|
import { OpenInviteDialogPayload } from "./dispatcher/payloads/OpenInviteDialogPayload";
|
||||||
|
|
||||||
export const PROTOCOL_PSTN = 'm.protocol.pstn';
|
export const PROTOCOL_PSTN = 'm.protocol.pstn';
|
||||||
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
|
export const PROTOCOL_PSTN_PREFIXED = 'im.vector.protocol.pstn';
|
||||||
|
@ -67,9 +69,6 @@ export const PROTOCOL_SIP_NATIVE = 'im.vector.protocol.sip_native';
|
||||||
export const PROTOCOL_SIP_VIRTUAL = 'im.vector.protocol.sip_virtual';
|
export const PROTOCOL_SIP_VIRTUAL = 'im.vector.protocol.sip_virtual';
|
||||||
|
|
||||||
const CHECK_PROTOCOLS_ATTEMPTS = 3;
|
const CHECK_PROTOCOLS_ATTEMPTS = 3;
|
||||||
// Event type for room account data and room creation content used to mark rooms as virtual rooms
|
|
||||||
// (and store the ID of their native room)
|
|
||||||
export const VIRTUAL_ROOM_EVENT_TYPE = 'im.vector.is_virtual_room';
|
|
||||||
|
|
||||||
enum AudioID {
|
enum AudioID {
|
||||||
Ring = 'ringAudio',
|
Ring = 'ringAudio',
|
||||||
|
@ -1094,14 +1093,17 @@ export default class CallHandler extends EventEmitter {
|
||||||
*/
|
*/
|
||||||
public showTransferDialog(call: MatrixCall): void {
|
public showTransferDialog(call: MatrixCall): void {
|
||||||
call.setRemoteOnHold(true);
|
call.setRemoteOnHold(true);
|
||||||
const { finished } = Modal.createTrackedDialog(
|
dis.dispatch<OpenInviteDialogPayload>({
|
||||||
'Transfer Call', '', InviteDialog, { kind: KIND_CALL_TRANSFER, call },
|
action: Action.OpenInviteDialog,
|
||||||
/*className=*/"mx_InviteDialog_transferWrapper", /*isPriority=*/false, /*isStatic=*/true,
|
kind: KIND_CALL_TRANSFER,
|
||||||
);
|
call,
|
||||||
finished.then((results: boolean[]) => {
|
analyticsName: "Transfer Call",
|
||||||
if (results.length === 0 || results[0] === false) {
|
className: "mx_InviteDialog_transferWrapper",
|
||||||
call.setRemoteOnHold(false);
|
onFinishedCallback: (results) => {
|
||||||
}
|
if (results.length === 0 || results[0] === false) {
|
||||||
|
call.setRemoteOnHold(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ limitations under the License.
|
||||||
import { MatrixClient } from "matrix-js-sdk/src/client";
|
import { MatrixClient } from "matrix-js-sdk/src/client";
|
||||||
import { IUploadOpts } from "matrix-js-sdk/src/@types/requests";
|
import { IUploadOpts } from "matrix-js-sdk/src/@types/requests";
|
||||||
import { MsgType } from "matrix-js-sdk/src/@types/event";
|
import { MsgType } from "matrix-js-sdk/src/@types/event";
|
||||||
import encrypt from "browser-encrypt-attachment";
|
import encrypt from "matrix-encrypt-attachment";
|
||||||
import extractPngChunks from "png-chunks-extract";
|
import extractPngChunks from "png-chunks-extract";
|
||||||
import { IAbortablePromise, IImageInfo } from "matrix-js-sdk/src/@types/partials";
|
import { IAbortablePromise, IImageInfo } from "matrix-js-sdk/src/@types/partials";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
@ -28,7 +28,6 @@ import { THREAD_RELATION_TYPE } from "matrix-js-sdk/src/models/thread";
|
||||||
|
|
||||||
import { IEncryptedFile, IMediaEventInfo } from "./customisations/models/IMediaEventContent";
|
import { IEncryptedFile, IMediaEventInfo } from "./customisations/models/IMediaEventContent";
|
||||||
import dis from './dispatcher/dispatcher';
|
import dis from './dispatcher/dispatcher';
|
||||||
import * as sdk from './index';
|
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import Spinner from "./components/views/elements/Spinner";
|
import Spinner from "./components/views/elements/Spinner";
|
||||||
|
@ -41,27 +40,23 @@ import {
|
||||||
UploadStartedPayload,
|
UploadStartedPayload,
|
||||||
} from "./dispatcher/payloads/UploadPayload";
|
} from "./dispatcher/payloads/UploadPayload";
|
||||||
import { IUpload } from "./models/IUpload";
|
import { IUpload } from "./models/IUpload";
|
||||||
import { BlurhashEncoder } from "./BlurhashEncoder";
|
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import { decorateStartSendingTime, sendRoundTripMetric } from "./sendTimePerformanceMetrics";
|
import { decorateStartSendingTime, sendRoundTripMetric } from "./sendTimePerformanceMetrics";
|
||||||
import { TimelineRenderingType } from "./contexts/RoomContext";
|
import { TimelineRenderingType } from "./contexts/RoomContext";
|
||||||
import RoomViewStore from "./stores/RoomViewStore";
|
import { RoomViewStore } from "./stores/RoomViewStore";
|
||||||
import { addReplyToMessageContent } from "./utils/Reply";
|
import { addReplyToMessageContent } from "./utils/Reply";
|
||||||
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
|
import UploadFailureDialog from "./components/views/dialogs/UploadFailureDialog";
|
||||||
|
import UploadConfirmDialog from "./components/views/dialogs/UploadConfirmDialog";
|
||||||
|
import { createThumbnail } from "./utils/image-media";
|
||||||
import { attachRelation } from "./components/views/rooms/SendMessageComposer";
|
import { attachRelation } from "./components/views/rooms/SendMessageComposer";
|
||||||
|
|
||||||
const MAX_WIDTH = 800;
|
|
||||||
const MAX_HEIGHT = 600;
|
|
||||||
|
|
||||||
// scraped out of a macOS hidpi (5660ppm) screenshot png
|
// scraped out of a macOS hidpi (5660ppm) screenshot png
|
||||||
// 5669 px (x-axis) , 5669 px (y-axis) , per metre
|
// 5669 px (x-axis) , 5669 px (y-axis) , per metre
|
||||||
const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
|
const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01];
|
||||||
|
|
||||||
export const BLURHASH_FIELD = "xyz.amorgan.blurhash"; // MSC2448
|
|
||||||
|
|
||||||
export class UploadCanceledError extends Error {}
|
export class UploadCanceledError extends Error {}
|
||||||
|
|
||||||
type ThumbnailableElement = HTMLImageElement | HTMLVideoElement;
|
|
||||||
|
|
||||||
interface IMediaConfig {
|
interface IMediaConfig {
|
||||||
"m.upload.size"?: number;
|
"m.upload.size"?: number;
|
||||||
}
|
}
|
||||||
|
@ -77,103 +72,6 @@ interface IContent {
|
||||||
url?: string;
|
url?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IThumbnail {
|
|
||||||
info: {
|
|
||||||
// eslint-disable-next-line camelcase
|
|
||||||
thumbnail_info: {
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
mimetype: string;
|
|
||||||
size: number;
|
|
||||||
};
|
|
||||||
w: number;
|
|
||||||
h: number;
|
|
||||||
[BLURHASH_FIELD]: string;
|
|
||||||
};
|
|
||||||
thumbnail: Blob;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a thumbnail for a image DOM element.
|
|
||||||
* The image will be smaller than MAX_WIDTH and MAX_HEIGHT.
|
|
||||||
* The thumbnail will have the same aspect ratio as the original.
|
|
||||||
* Draws the element into a canvas using CanvasRenderingContext2D.drawImage
|
|
||||||
* Then calls Canvas.toBlob to get a blob object for the image data.
|
|
||||||
*
|
|
||||||
* Since it needs to calculate the dimensions of the source image and the
|
|
||||||
* thumbnailed image it returns an info object filled out with information
|
|
||||||
* about the original image and the thumbnail.
|
|
||||||
*
|
|
||||||
* @param {HTMLElement} element The element to thumbnail.
|
|
||||||
* @param {number} inputWidth The width of the image in the input element.
|
|
||||||
* @param {number} inputHeight the width of the image in the input element.
|
|
||||||
* @param {string} mimeType The mimeType to save the blob as.
|
|
||||||
* @param {boolean} calculateBlurhash Whether to calculate a blurhash of the given image too.
|
|
||||||
* @return {Promise} A promise that resolves with an object with an info key
|
|
||||||
* and a thumbnail key.
|
|
||||||
*/
|
|
||||||
export async function createThumbnail(
|
|
||||||
element: ThumbnailableElement,
|
|
||||||
inputWidth: number,
|
|
||||||
inputHeight: number,
|
|
||||||
mimeType: string,
|
|
||||||
calculateBlurhash = true,
|
|
||||||
): Promise<IThumbnail> {
|
|
||||||
let targetWidth = inputWidth;
|
|
||||||
let targetHeight = inputHeight;
|
|
||||||
if (targetHeight > MAX_HEIGHT) {
|
|
||||||
targetWidth = Math.floor(targetWidth * (MAX_HEIGHT / targetHeight));
|
|
||||||
targetHeight = MAX_HEIGHT;
|
|
||||||
}
|
|
||||||
if (targetWidth > MAX_WIDTH) {
|
|
||||||
targetHeight = Math.floor(targetHeight * (MAX_WIDTH / targetWidth));
|
|
||||||
targetWidth = MAX_WIDTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
let canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
||||||
let context: CanvasRenderingContext2D;
|
|
||||||
try {
|
|
||||||
canvas = new window.OffscreenCanvas(targetWidth, targetHeight);
|
|
||||||
context = canvas.getContext("2d");
|
|
||||||
} catch (e) {
|
|
||||||
// Fallback support for other browsers (Safari and Firefox for now)
|
|
||||||
canvas = document.createElement("canvas");
|
|
||||||
(canvas as HTMLCanvasElement).width = targetWidth;
|
|
||||||
(canvas as HTMLCanvasElement).height = targetHeight;
|
|
||||||
context = canvas.getContext("2d");
|
|
||||||
}
|
|
||||||
|
|
||||||
context.drawImage(element, 0, 0, targetWidth, targetHeight);
|
|
||||||
|
|
||||||
let thumbnailPromise: Promise<Blob>;
|
|
||||||
|
|
||||||
if (window.OffscreenCanvas) {
|
|
||||||
thumbnailPromise = (canvas as OffscreenCanvas).convertToBlob({ type: mimeType });
|
|
||||||
} else {
|
|
||||||
thumbnailPromise = new Promise<Blob>(resolve => (canvas as HTMLCanvasElement).toBlob(resolve, mimeType));
|
|
||||||
}
|
|
||||||
|
|
||||||
const imageData = context.getImageData(0, 0, targetWidth, targetHeight);
|
|
||||||
// thumbnailPromise and blurhash promise are being awaited concurrently
|
|
||||||
const blurhash = calculateBlurhash ? await BlurhashEncoder.instance.getBlurhash(imageData) : undefined;
|
|
||||||
const thumbnail = await thumbnailPromise;
|
|
||||||
|
|
||||||
return {
|
|
||||||
info: {
|
|
||||||
thumbnail_info: {
|
|
||||||
w: targetWidth,
|
|
||||||
h: targetHeight,
|
|
||||||
mimetype: thumbnail.type,
|
|
||||||
size: thumbnail.size,
|
|
||||||
},
|
|
||||||
w: inputWidth,
|
|
||||||
h: inputHeight,
|
|
||||||
[BLURHASH_FIELD]: blurhash,
|
|
||||||
},
|
|
||||||
thumbnail,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a file into a newly created image element.
|
* Load a file into a newly created image element.
|
||||||
*
|
*
|
||||||
|
@ -472,7 +370,7 @@ export default class ContentMessages {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const replyToEvent = RoomViewStore.getQuotingEvent();
|
const replyToEvent = RoomViewStore.instance.getQuotingEvent();
|
||||||
if (!this.mediaConfig) { // hot-path optimization to not flash a spinner if we don't need to
|
if (!this.mediaConfig) { // hot-path optimization to not flash a spinner if we don't need to
|
||||||
const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
|
const modal = Modal.createDialog(Spinner, null, 'mx_Dialog_spinner');
|
||||||
await this.ensureMediaConfigFetched(matrixClient);
|
await this.ensureMediaConfigFetched(matrixClient);
|
||||||
|
@ -491,8 +389,6 @@ export default class ContentMessages {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tooBigFiles.length > 0) {
|
if (tooBigFiles.length > 0) {
|
||||||
// FIXME: Using an import will result in Element crashing
|
|
||||||
const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog");
|
|
||||||
const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, {
|
const { finished } = Modal.createTrackedDialog<[boolean]>('Upload Failure', '', UploadFailureDialog, {
|
||||||
badFiles: tooBigFiles,
|
badFiles: tooBigFiles,
|
||||||
totalFiles: files.length,
|
totalFiles: files.length,
|
||||||
|
@ -509,8 +405,6 @@ export default class ContentMessages {
|
||||||
for (let i = 0; i < okFiles.length; ++i) {
|
for (let i = 0; i < okFiles.length; ++i) {
|
||||||
const file = okFiles[i];
|
const file = okFiles[i];
|
||||||
if (!uploadAll) {
|
if (!uploadAll) {
|
||||||
// FIXME: Using an import will result in Element crashing
|
|
||||||
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
|
|
||||||
const { finished } = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
|
const { finished } = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
|
||||||
'', UploadConfirmDialog, {
|
'', UploadConfirmDialog, {
|
||||||
file,
|
file,
|
||||||
|
@ -689,8 +583,6 @@ export default class ContentMessages {
|
||||||
{ fileName: upload.fileName },
|
{ fileName: upload.fileName },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// FIXME: Using an import will result in Element crashing
|
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
|
||||||
Modal.createTrackedDialog('Upload failed', '', ErrorDialog, {
|
Modal.createTrackedDialog('Upload failed', '', ErrorDialog, {
|
||||||
title: _t('Upload Failed'),
|
title: _t('Upload Failed'),
|
||||||
description: desc,
|
description: desc,
|
||||||
|
|
|
@ -36,9 +36,9 @@ import {
|
||||||
} from "./toasts/UnverifiedSessionToast";
|
} from "./toasts/UnverifiedSessionToast";
|
||||||
import { accessSecretStorage, isSecretStorageBeingAccessed } from "./SecurityManager";
|
import { accessSecretStorage, isSecretStorageBeingAccessed } from "./SecurityManager";
|
||||||
import { isSecureBackupRequired } from './utils/WellKnownUtils';
|
import { isSecureBackupRequired } from './utils/WellKnownUtils';
|
||||||
import { isLoggedIn } from './components/structures/MatrixChat';
|
|
||||||
import { ActionPayload } from "./dispatcher/payloads";
|
import { ActionPayload } from "./dispatcher/payloads";
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
|
import { isLoggedIn } from "./utils/login";
|
||||||
|
|
||||||
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
const KEY_BACKUP_POLL_INTERVAL = 5 * 60 * 1000;
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,19 @@ import SessionRestoreErrorDialog from "./components/views/dialogs/SessionRestore
|
||||||
import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog";
|
import StorageEvictedDialog from "./components/views/dialogs/StorageEvictedDialog";
|
||||||
import { setSentryUser } from "./sentry";
|
import { setSentryUser } from "./sentry";
|
||||||
import SdkConfig from "./SdkConfig";
|
import SdkConfig from "./SdkConfig";
|
||||||
|
import { DialogOpener } from "./utils/DialogOpener";
|
||||||
|
import { Action } from "./dispatcher/actions";
|
||||||
|
|
||||||
const HOMESERVER_URL_KEY = "mx_hs_url";
|
const HOMESERVER_URL_KEY = "mx_hs_url";
|
||||||
const ID_SERVER_URL_KEY = "mx_is_url";
|
const ID_SERVER_URL_KEY = "mx_is_url";
|
||||||
|
|
||||||
|
dis.register((payload) => {
|
||||||
|
if (payload.action === Action.TriggerLogout) {
|
||||||
|
// noinspection JSIgnoredPromiseFromCall - we don't care if it fails
|
||||||
|
onLoggedOut();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
interface ILoadSessionOpts {
|
interface ILoadSessionOpts {
|
||||||
enableGuest?: boolean;
|
enableGuest?: boolean;
|
||||||
guestHsUrl?: string;
|
guestHsUrl?: string;
|
||||||
|
@ -791,6 +800,7 @@ async function startMatrixClient(startSyncing = true): Promise<void> {
|
||||||
TypingStore.sharedInstance().reset();
|
TypingStore.sharedInstance().reset();
|
||||||
ToastStore.sharedInstance().reset();
|
ToastStore.sharedInstance().reset();
|
||||||
|
|
||||||
|
DialogOpener.instance.prepare();
|
||||||
Notifier.start();
|
Notifier.start();
|
||||||
UserActivity.sharedInstance().start();
|
UserActivity.sharedInstance().start();
|
||||||
DMRoomMap.makeShared().start();
|
DMRoomMap.makeShared().start();
|
||||||
|
@ -865,8 +875,9 @@ async function clearStorage(opts?: { deleteEverything?: boolean }): Promise<void
|
||||||
Analytics.disable();
|
Analytics.disable();
|
||||||
|
|
||||||
if (window.localStorage) {
|
if (window.localStorage) {
|
||||||
// try to save any 3pid invites from being obliterated
|
// try to save any 3pid invites from being obliterated and registration time
|
||||||
const pendingInvites = ThreepidInviteStore.instance.getWireInvites();
|
const pendingInvites = ThreepidInviteStore.instance.getWireInvites();
|
||||||
|
const registrationTime = window.localStorage.getItem("mx_registration_time");
|
||||||
|
|
||||||
window.localStorage.clear();
|
window.localStorage.clear();
|
||||||
|
|
||||||
|
@ -876,13 +887,17 @@ async function clearStorage(opts?: { deleteEverything?: boolean }): Promise<void
|
||||||
logger.error("idbDelete failed for account:mx_access_token", e);
|
logger.error("idbDelete failed for account:mx_access_token", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now restore those invites
|
// now restore those invites and registration time
|
||||||
if (!opts?.deleteEverything) {
|
if (!opts?.deleteEverything) {
|
||||||
pendingInvites.forEach(i => {
|
pendingInvites.forEach(i => {
|
||||||
const roomId = i.roomId;
|
const roomId = i.roomId;
|
||||||
delete i.roomId; // delete to avoid confusing the store
|
delete i.roomId; // delete to avoid confusing the store
|
||||||
ThreepidInviteStore.instance.storeInvite(roomId, i);
|
ThreepidInviteStore.instance.storeInvite(roomId, i);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (registrationTime) {
|
||||||
|
window.localStorage.setItem("mx_registration_time", registrationTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import { verificationMethods } from 'matrix-js-sdk/src/crypto';
|
||||||
import { SHOW_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
|
import { SHOW_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
|
||||||
import * as sdk from './index';
|
|
||||||
import createMatrixClient from './utils/createMatrixClient';
|
import createMatrixClient from './utils/createMatrixClient';
|
||||||
import SettingsStore from './settings/SettingsStore';
|
import SettingsStore from './settings/SettingsStore';
|
||||||
import MatrixActionCreators from './actions/MatrixActionCreators';
|
import MatrixActionCreators from './actions/MatrixActionCreators';
|
||||||
|
@ -37,6 +36,7 @@ import * as StorageManager from './utils/StorageManager';
|
||||||
import IdentityAuthClient from './IdentityAuthClient';
|
import IdentityAuthClient from './IdentityAuthClient';
|
||||||
import { crossSigningCallbacks, tryToUnlockSecretStorageWithDehydrationKey } from './SecurityManager';
|
import { crossSigningCallbacks, tryToUnlockSecretStorageWithDehydrationKey } from './SecurityManager';
|
||||||
import SecurityCustomisations from "./customisations/Security";
|
import SecurityCustomisations from "./customisations/Security";
|
||||||
|
import CryptoStoreTooNewDialog from "./components/views/dialogs/CryptoStoreTooNewDialog";
|
||||||
|
|
||||||
export interface IMatrixClientCreds {
|
export interface IMatrixClientCreds {
|
||||||
homeserverUrl: string;
|
homeserverUrl: string;
|
||||||
|
@ -117,7 +117,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
};
|
};
|
||||||
|
|
||||||
private matrixClient: MatrixClient = null;
|
private matrixClient: MatrixClient = null;
|
||||||
private justRegisteredUserId: string;
|
private justRegisteredUserId: string | null = null;
|
||||||
|
|
||||||
// the credentials used to init the current client object.
|
// the credentials used to init the current client object.
|
||||||
// used if we tear it down & recreate it with a different store
|
// used if we tear it down & recreate it with a different store
|
||||||
|
@ -136,7 +136,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
MatrixActionCreators.stop();
|
MatrixActionCreators.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
public setJustRegisteredUserId(uid: string): void {
|
public setJustRegisteredUserId(uid: string | null): void {
|
||||||
this.justRegisteredUserId = uid;
|
this.justRegisteredUserId = uid;
|
||||||
if (uid) {
|
if (uid) {
|
||||||
window.localStorage.setItem("mx_registration_time", String(new Date().getTime()));
|
window.localStorage.setItem("mx_registration_time", String(new Date().getTime()));
|
||||||
|
@ -151,9 +151,14 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
}
|
}
|
||||||
|
|
||||||
public userRegisteredWithinLastHours(hours: number): boolean {
|
public userRegisteredWithinLastHours(hours: number): boolean {
|
||||||
|
if (hours <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const date = new Date(window.localStorage.getItem("mx_registration_time"));
|
const registrationTime = parseInt(window.localStorage.getItem("mx_registration_time"));
|
||||||
return ((new Date().getTime() - date.getTime()) / 36e5) <= hours;
|
const diff = Date.now() - registrationTime;
|
||||||
|
return (diff / 36e5) <= hours;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -200,9 +205,6 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e && e.name === 'InvalidCryptoStoreError') {
|
if (e && e.name === 'InvalidCryptoStoreError') {
|
||||||
// The js-sdk found a crypto DB too new for it to use
|
// The js-sdk found a crypto DB too new for it to use
|
||||||
// FIXME: Using an import will result in test failures
|
|
||||||
const CryptoStoreTooNewDialog =
|
|
||||||
sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog");
|
|
||||||
Modal.createDialog(CryptoStoreTooNewDialog);
|
Modal.createDialog(CryptoStoreTooNewDialog);
|
||||||
}
|
}
|
||||||
// this can happen for a number of reasons, the most likely being
|
// this can happen for a number of reasons, the most likely being
|
||||||
|
|
|
@ -37,7 +37,7 @@ import SettingsStore from "./settings/SettingsStore";
|
||||||
import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast";
|
import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast";
|
||||||
import { SettingLevel } from "./settings/SettingLevel";
|
import { SettingLevel } from "./settings/SettingLevel";
|
||||||
import { isPushNotifyDisabled } from "./settings/controllers/NotificationControllers";
|
import { isPushNotifyDisabled } from "./settings/controllers/NotificationControllers";
|
||||||
import RoomViewStore from "./stores/RoomViewStore";
|
import { RoomViewStore } from "./stores/RoomViewStore";
|
||||||
import UserActivity from "./UserActivity";
|
import UserActivity from "./UserActivity";
|
||||||
import { mediaFromMxc } from "./customisations/Media";
|
import { mediaFromMxc } from "./customisations/Media";
|
||||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
|
@ -401,7 +401,7 @@ export const Notifier = {
|
||||||
|
|
||||||
const actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
const actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
|
||||||
if (actions?.notify) {
|
if (actions?.notify) {
|
||||||
if (RoomViewStore.getRoomId() === room.roomId &&
|
if (RoomViewStore.instance.getRoomId() === room.roomId &&
|
||||||
UserActivity.sharedInstance().userActiveRecently() &&
|
UserActivity.sharedInstance().userActiveRecently() &&
|
||||||
!Modal.hasDialogs()
|
!Modal.hasDialogs()
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -24,10 +24,12 @@ import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import MultiInviter, { CompletionStates } from './utils/MultiInviter';
|
import MultiInviter, { CompletionStates } from './utils/MultiInviter';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import InviteDialog, { KIND_DM, KIND_INVITE, Member } from "./components/views/dialogs/InviteDialog";
|
import InviteDialog from "./components/views/dialogs/InviteDialog";
|
||||||
import BaseAvatar from "./components/views/avatars/BaseAvatar";
|
import BaseAvatar from "./components/views/avatars/BaseAvatar";
|
||||||
import { mediaFromMxc } from "./customisations/Media";
|
import { mediaFromMxc } from "./customisations/Media";
|
||||||
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
import ErrorDialog from "./components/views/dialogs/ErrorDialog";
|
||||||
|
import { KIND_DM, KIND_INVITE } from "./components/views/dialogs/InviteDialogTypes";
|
||||||
|
import { Member } from "./utils/direct-messages";
|
||||||
|
|
||||||
export interface IInviteResult {
|
export interface IInviteResult {
|
||||||
states: CompletionStates;
|
states: CompletionStates;
|
||||||
|
|
48
src/Rooms.ts
|
@ -19,9 +19,6 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import AliasCustomisations from './customisations/Alias';
|
import AliasCustomisations from './customisations/Alias';
|
||||||
import DMRoomMap from "./utils/DMRoomMap";
|
|
||||||
import SpaceStore from "./stores/spaces/SpaceStore";
|
|
||||||
import { _t } from "./languageHandler";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a room object, return the alias we should use for it,
|
* Given a room object, return the alias we should use for it,
|
||||||
|
@ -157,48 +154,3 @@ function guessDMRoomTargetId(room: Room, myUserId: string): string {
|
||||||
if (oldestUser === undefined) return myUserId;
|
if (oldestUser === undefined) return myUserId;
|
||||||
return oldestUser.userId;
|
return oldestUser.userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spaceContextDetailsText(space: Room): string {
|
|
||||||
if (!space.isSpaceRoom()) return undefined;
|
|
||||||
|
|
||||||
const [parent, secondParent, ...otherParents] = SpaceStore.instance.getKnownParents(space.roomId);
|
|
||||||
if (secondParent && !otherParents?.length) {
|
|
||||||
// exactly 2 edge case for improved i18n
|
|
||||||
return _t("%(space1Name)s and %(space2Name)s", {
|
|
||||||
space1Name: space.client.getRoom(parent)?.name,
|
|
||||||
space2Name: space.client.getRoom(secondParent)?.name,
|
|
||||||
});
|
|
||||||
} else if (parent) {
|
|
||||||
return _t("%(spaceName)s and %(count)s others", {
|
|
||||||
spaceName: space.client.getRoom(parent)?.name,
|
|
||||||
count: otherParents.length,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return space.getCanonicalAlias();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function roomContextDetailsText(room: Room): string {
|
|
||||||
if (room.isSpaceRoom()) return undefined;
|
|
||||||
|
|
||||||
const dmPartner = DMRoomMap.shared().getUserIdForRoomId(room.roomId);
|
|
||||||
if (dmPartner) {
|
|
||||||
return dmPartner;
|
|
||||||
}
|
|
||||||
|
|
||||||
const [parent, secondParent, ...otherParents] = SpaceStore.instance.getKnownParents(room.roomId);
|
|
||||||
if (secondParent && !otherParents?.length) {
|
|
||||||
// exactly 2 edge case for improved i18n
|
|
||||||
return _t("%(space1Name)s and %(space2Name)s", {
|
|
||||||
space1Name: room.client.getRoom(parent)?.name,
|
|
||||||
space2Name: room.client.getRoom(secondParent)?.name,
|
|
||||||
});
|
|
||||||
} else if (parent) {
|
|
||||||
return _t("%(spaceName)s and %(count)s others", {
|
|
||||||
spaceName: room.client.getRoom(parent)?.name,
|
|
||||||
count: otherParents.length,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return room.getCanonicalAlias();
|
|
||||||
}
|
|
||||||
|
|
|
@ -241,7 +241,7 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import dis from './dispatcher/dispatcher';
|
import dis from './dispatcher/dispatcher';
|
||||||
import WidgetUtils from './utils/WidgetUtils';
|
import WidgetUtils from './utils/WidgetUtils';
|
||||||
import RoomViewStore from './stores/RoomViewStore';
|
import { RoomViewStore } from './stores/RoomViewStore';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import { IntegrationManagers } from "./integrations/IntegrationManagers";
|
import { IntegrationManagers } from "./integrations/IntegrationManagers";
|
||||||
import { WidgetType } from "./widgets/WidgetType";
|
import { WidgetType } from "./widgets/WidgetType";
|
||||||
|
@ -638,7 +638,7 @@ const onMessage = function(event: MessageEvent<any>): void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roomId !== RoomViewStore.getRoomId()) {
|
if (roomId !== RoomViewStore.instance.getRoomId()) {
|
||||||
sendError(event, _t('Room %(roomId)s not visible', { roomId: roomId }));
|
sendError(event, _t('Room %(roomId)s not visible', { roomId: roomId }));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import { logger } from "matrix-js-sdk/src/logger";
|
||||||
import { ComponentType } from "react";
|
import { ComponentType } from "react";
|
||||||
|
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
import * as sdk from './index';
|
|
||||||
import { MatrixClientPeg } from './MatrixClientPeg';
|
import { MatrixClientPeg } from './MatrixClientPeg';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
import { isSecureBackupRequired } from './utils/WellKnownUtils';
|
import { isSecureBackupRequired } from './utils/WellKnownUtils';
|
||||||
|
@ -34,6 +33,7 @@ import RestoreKeyBackupDialog from './components/views/dialogs/security/RestoreK
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import SecurityCustomisations from "./customisations/Security";
|
import SecurityCustomisations from "./customisations/Security";
|
||||||
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
||||||
|
import InteractiveAuthDialog from "./components/views/dialogs/InteractiveAuthDialog";
|
||||||
|
|
||||||
// This stores the secret storage private keys in memory for the JS SDK. This is
|
// This stores the secret storage private keys in memory for the JS SDK. This is
|
||||||
// only meant to act as a cache to avoid prompting the user multiple times
|
// only meant to act as a cache to avoid prompting the user multiple times
|
||||||
|
@ -360,8 +360,6 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f
|
||||||
throw new Error("Secret storage creation canceled");
|
throw new Error("Secret storage creation canceled");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FIXME: Using an import will result in test failures
|
|
||||||
const InteractiveAuthDialog = sdk.getComponent("dialogs.InteractiveAuthDialog");
|
|
||||||
await cli.bootstrapCrossSigning({
|
await cli.bootstrapCrossSigning({
|
||||||
authUploadDeviceSigningKeys: async (makeRequest) => {
|
authUploadDeviceSigningKeys: async (makeRequest) => {
|
||||||
const { finished } = Modal.createTrackedDialog(
|
const { finished } = Modal.createTrackedDialog(
|
||||||
|
|