Merge remote-tracking branch 'upstream/develop' into fix/17130/draggable-pip

This commit is contained in:
Šimon Brandner 2021-06-17 15:41:19 +02:00
commit d89392a1d9
No known key found for this signature in database
GPG key ID: 9760693FDD98A790
418 changed files with 17369 additions and 6592 deletions

View file

@ -15,11 +15,10 @@ module.exports = {
"prefer-promise-reject-errors": "off",
"no-async-promise-executor": "off",
"quotes": "off",
"indent": "off",
},
overrides: [{
"files": ["src/**/*.{ts,tsx}"],
"files": ["src/**/*.{ts,tsx}", "test/**/*.{ts,tsx}"],
"extends": ["matrix-org/ts"],
"rules": {
// We're okay being explicit at the moment
@ -31,6 +30,24 @@ module.exports = {
"quotes": "off",
"no-extra-boolean-cast": "off",
"no-restricted-properties": [
"error",
...buildRestrictedPropertiesOptions(
["window.innerHeight", "window.innerWidth", "window.visualViewport"],
"Use UIStore to access window dimensions instead",
),
],
},
}],
};
function buildRestrictedPropertiesOptions(properties, message) {
return properties.map(prop => {
const [object, property] = prop.split(".");
return {
object,
property,
message,
};
});
}

38
.github/workflows/develop.yml vendored Normal file
View file

@ -0,0 +1,38 @@
name: Develop
on:
push:
branches: [develop]
pull_request:
branches: [develop]
jobs:
end-to-end:
runs-on: ubuntu-latest
container: vectorim/element-web-ci-e2etests-env:latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: End-to-End tests
run: ./scripts/ci/end-to-end-tests.sh
- name: Archive logs
uses: actions/upload-artifact@v2
with:
path: |
test/end-to-end-tests/logs/**/*
test/end-to-end-tests/synapse/installations/consent/homeserver.log
retention-days: 14
- name: Download previous benchmark data
uses: actions/cache@v1
with:
path: ./cache
key: ${{ runner.os }}-benchmark
- name: Store benchmark result
uses: matrix-org/github-action-benchmark@jsperfentry-1
with:
tool: 'jsperformanceentry'
output-file-path: test/end-to-end-tests/performance-entries.json
fail-on-alert: false
comment-on-alert: false
# Only temporary to monitor where failures occur
alert-comment-cc-users: '@gsouquet'
github-token: ${{ secrets.DEPLOY_GH_PAGES }}
auto-push: ${{ github.ref == 'refs/heads/develop' }}

View file

@ -1,3 +1,444 @@
Changes in [3.23.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.23.0) (2021-06-07)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.23.0-rc.1...v3.23.0)
* Upgrade to JS SDK 11.2.0
* [Release] Fix notif panel timestamp padding
[\#6158](https://github.com/matrix-org/matrix-react-sdk/pull/6158)
Changes in [3.23.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.23.0-rc.1) (2021-06-01)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.22.0...v3.23.0-rc.1)
* Upgrade to JS SDK 11.2.0-rc.1
* Translations update from Weblate
[\#6128](https://github.com/matrix-org/matrix-react-sdk/pull/6128)
* Fix all DMs wrongly appearing in room list when `m.direct` is changed
[\#6122](https://github.com/matrix-org/matrix-react-sdk/pull/6122)
* Update way of checking for registration disabled
[\#6123](https://github.com/matrix-org/matrix-react-sdk/pull/6123)
* Fix the ability to remove avatar from a space via settings
[\#6126](https://github.com/matrix-org/matrix-react-sdk/pull/6126)
* Switch to stable endpoint/fields for MSC2858
[\#6125](https://github.com/matrix-org/matrix-react-sdk/pull/6125)
* Clear stored editor state when canceling editing using a shortcut
[\#6117](https://github.com/matrix-org/matrix-react-sdk/pull/6117)
* Respect newlines in space topics
[\#6124](https://github.com/matrix-org/matrix-react-sdk/pull/6124)
* Add url param `defaultUsername` to prefill the login username field
[\#5674](https://github.com/matrix-org/matrix-react-sdk/pull/5674)
* Bump ws from 7.4.2 to 7.4.6
[\#6115](https://github.com/matrix-org/matrix-react-sdk/pull/6115)
* Sticky headers repositioning without layout trashing
[\#6110](https://github.com/matrix-org/matrix-react-sdk/pull/6110)
* Handle user_busy in voip calls
[\#6112](https://github.com/matrix-org/matrix-react-sdk/pull/6112)
* Avoid showing warning modals from the invite dialog after it unmounts
[\#6105](https://github.com/matrix-org/matrix-react-sdk/pull/6105)
* Fix misleading child counts in spaces
[\#6109](https://github.com/matrix-org/matrix-react-sdk/pull/6109)
* Close creation menu when expanding space panel via expand hierarchy
[\#6090](https://github.com/matrix-org/matrix-react-sdk/pull/6090)
* Prevent having duplicates in pending room state
[\#6108](https://github.com/matrix-org/matrix-react-sdk/pull/6108)
* Update reactions row on event decryption
[\#6106](https://github.com/matrix-org/matrix-react-sdk/pull/6106)
* Destroy playback instance on voice message unmount
[\#6101](https://github.com/matrix-org/matrix-react-sdk/pull/6101)
* Fix message preview not up to date
[\#6102](https://github.com/matrix-org/matrix-react-sdk/pull/6102)
* Convert some Flow typed files to TS (round 2)
[\#6076](https://github.com/matrix-org/matrix-react-sdk/pull/6076)
* Remove unused middlePanelResized event listener
[\#6086](https://github.com/matrix-org/matrix-react-sdk/pull/6086)
* Fix accessing currentState on an invalid joinedRoom
[\#6100](https://github.com/matrix-org/matrix-react-sdk/pull/6100)
* Remove Promise allSettled polyfill as js-sdk uses it directly
[\#6097](https://github.com/matrix-org/matrix-react-sdk/pull/6097)
* Prevent DecoratedRoomAvatar to update its state for the same value
[\#6099](https://github.com/matrix-org/matrix-react-sdk/pull/6099)
* Skip generatePreview if event is not part of the live timeline
[\#6098](https://github.com/matrix-org/matrix-react-sdk/pull/6098)
* fix sticky headers when results num get displayed
[\#6095](https://github.com/matrix-org/matrix-react-sdk/pull/6095)
* Improve addEventsToTimeline performance scoping WhoIsTypingTile::setState
[\#6094](https://github.com/matrix-org/matrix-react-sdk/pull/6094)
* Safeguards to prevent layout trashing for window dimensions
[\#6092](https://github.com/matrix-org/matrix-react-sdk/pull/6092)
* Use local room state to render space hierarchy if the room is known
[\#6089](https://github.com/matrix-org/matrix-react-sdk/pull/6089)
* Add spinner in UserMenu to list pending long running actions
[\#6085](https://github.com/matrix-org/matrix-react-sdk/pull/6085)
* Stop overscroll in Firefox Nightly for macOS
[\#6093](https://github.com/matrix-org/matrix-react-sdk/pull/6093)
* Move SettingsStore watchers/monitors over to ES6 maps for performance
[\#6063](https://github.com/matrix-org/matrix-react-sdk/pull/6063)
* Bump libolm version.
[\#6080](https://github.com/matrix-org/matrix-react-sdk/pull/6080)
* Improve styling of the message action bar
[\#6066](https://github.com/matrix-org/matrix-react-sdk/pull/6066)
* Improve explore rooms when no results are found
[\#6070](https://github.com/matrix-org/matrix-react-sdk/pull/6070)
* Remove logo spinner
[\#6078](https://github.com/matrix-org/matrix-react-sdk/pull/6078)
* Fix add reaction prompt showing even when user is not joined to room
[\#6073](https://github.com/matrix-org/matrix-react-sdk/pull/6073)
* Vectorize spinners
[\#5680](https://github.com/matrix-org/matrix-react-sdk/pull/5680)
* Fix handling of via servers for suggested rooms
[\#6077](https://github.com/matrix-org/matrix-react-sdk/pull/6077)
* Upgrade showChatEffects to room-level setting exposure
[\#6075](https://github.com/matrix-org/matrix-react-sdk/pull/6075)
* Delete RoomView dead code
[\#6071](https://github.com/matrix-org/matrix-react-sdk/pull/6071)
* Reduce noise in tests
[\#6074](https://github.com/matrix-org/matrix-react-sdk/pull/6074)
* Fix room name issues in right panel summary card
[\#6069](https://github.com/matrix-org/matrix-react-sdk/pull/6069)
* Cache normalized room name
[\#6072](https://github.com/matrix-org/matrix-react-sdk/pull/6072)
* Update MemberList to reflect changes for invite permission change
[\#6061](https://github.com/matrix-org/matrix-react-sdk/pull/6061)
* Delete RoomView dead code
[\#6065](https://github.com/matrix-org/matrix-react-sdk/pull/6065)
* Show subspace rooms count even if it is 0 for consistency
[\#6067](https://github.com/matrix-org/matrix-react-sdk/pull/6067)
Changes in [3.22.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.22.0) (2021-05-24)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.22.0-rc.1...v3.22.0)
* Upgrade to JS SDK 11.1.0
* [Release] Bump libolm version
[\#6087](https://github.com/matrix-org/matrix-react-sdk/pull/6087)
Changes in [3.22.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.22.0-rc.1) (2021-05-19)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.21.0...v3.22.0-rc.1)
* Upgrade to JS SDK 11.1.0-rc.1
* Translations update from Weblate
[\#6068](https://github.com/matrix-org/matrix-react-sdk/pull/6068)
* Show DMs in space for invited members too, to match Android impl
[\#6062](https://github.com/matrix-org/matrix-react-sdk/pull/6062)
* Support filtering by alias in add existing to space dialog
[\#6057](https://github.com/matrix-org/matrix-react-sdk/pull/6057)
* Fix issue when a room without a name or alias is marked as suggested
[\#6064](https://github.com/matrix-org/matrix-react-sdk/pull/6064)
* Fix space room hierarchy not updating when removing a room
[\#6055](https://github.com/matrix-org/matrix-react-sdk/pull/6055)
* Revert "Try putting room list handling behind a lock"
[\#6060](https://github.com/matrix-org/matrix-react-sdk/pull/6060)
* Stop assuming encrypted messages are decrypted ahead of time
[\#6052](https://github.com/matrix-org/matrix-react-sdk/pull/6052)
* Add error detail when languges fail to load
[\#6059](https://github.com/matrix-org/matrix-react-sdk/pull/6059)
* Add space invaders chat effect
[\#6053](https://github.com/matrix-org/matrix-react-sdk/pull/6053)
* Create SpaceProvider and hide Spaces from the RoomProvider autocompleter
[\#6051](https://github.com/matrix-org/matrix-react-sdk/pull/6051)
* Don't mark a room as unread when redacted event is present
[\#6049](https://github.com/matrix-org/matrix-react-sdk/pull/6049)
* Add support for MSC2873: Client information for Widgets
[\#6023](https://github.com/matrix-org/matrix-react-sdk/pull/6023)
* Support UI for MSC2762: Widgets reading events from rooms
[\#5960](https://github.com/matrix-org/matrix-react-sdk/pull/5960)
* Fix crash on opening notification panel
[\#6047](https://github.com/matrix-org/matrix-react-sdk/pull/6047)
* Remove custom LoggedInView::shouldComponentUpdate logic
[\#6046](https://github.com/matrix-org/matrix-react-sdk/pull/6046)
* Fix edge cases with the new add reactions prompt button
[\#6045](https://github.com/matrix-org/matrix-react-sdk/pull/6045)
* Add ids to homeserver and passphrase fields
[\#6043](https://github.com/matrix-org/matrix-react-sdk/pull/6043)
* Update space order field validity requirements to match msc update
[\#6042](https://github.com/matrix-org/matrix-react-sdk/pull/6042)
* Try putting room list handling behind a lock
[\#6024](https://github.com/matrix-org/matrix-react-sdk/pull/6024)
* Improve progress bar progression for smaller voice messages
[\#6035](https://github.com/matrix-org/matrix-react-sdk/pull/6035)
* Fix share space edge case where space is public but not invitable
[\#6039](https://github.com/matrix-org/matrix-react-sdk/pull/6039)
* Add missing 'rel' to image view download button
[\#6033](https://github.com/matrix-org/matrix-react-sdk/pull/6033)
* Improve visible waveform for voice messages
[\#6034](https://github.com/matrix-org/matrix-react-sdk/pull/6034)
* Fix roving tab index intercepting home/end in space create menu
[\#6040](https://github.com/matrix-org/matrix-react-sdk/pull/6040)
* Decorate room avatars with publicity in add existing to space flow
[\#6030](https://github.com/matrix-org/matrix-react-sdk/pull/6030)
* Improve Spaces "Just Me" wizard
[\#6025](https://github.com/matrix-org/matrix-react-sdk/pull/6025)
* Increase hover feedback on room sub list buttons
[\#6037](https://github.com/matrix-org/matrix-react-sdk/pull/6037)
* Show alternative button during space creation wizard if no rooms
[\#6029](https://github.com/matrix-org/matrix-react-sdk/pull/6029)
* Swap rotation buttons in the image viewer
[\#6032](https://github.com/matrix-org/matrix-react-sdk/pull/6032)
* Typo: initilisation -> initialisation
[\#5915](https://github.com/matrix-org/matrix-react-sdk/pull/5915)
* Save edited state of a message when switching rooms
[\#6001](https://github.com/matrix-org/matrix-react-sdk/pull/6001)
* Fix shield icon in Untrusted Device Dialog
[\#6022](https://github.com/matrix-org/matrix-react-sdk/pull/6022)
* Do not eagerly decrypt breadcrumb rooms
[\#6028](https://github.com/matrix-org/matrix-react-sdk/pull/6028)
* Update spaces.png
[\#6031](https://github.com/matrix-org/matrix-react-sdk/pull/6031)
* Encourage more diverse reactions to content
[\#6027](https://github.com/matrix-org/matrix-react-sdk/pull/6027)
* Wrap decodeURIComponent in try-catch to protect against malformed URIs
[\#6026](https://github.com/matrix-org/matrix-react-sdk/pull/6026)
* Iterate beta feedback dialog
[\#6021](https://github.com/matrix-org/matrix-react-sdk/pull/6021)
* Disable space fields whilst their form is busy
[\#6020](https://github.com/matrix-org/matrix-react-sdk/pull/6020)
* Add missing space on beta feedback dialog
[\#6018](https://github.com/matrix-org/matrix-react-sdk/pull/6018)
* Fix colours used for the back button in space create menu
[\#6017](https://github.com/matrix-org/matrix-react-sdk/pull/6017)
* Prioritise and reduce the amount of events decrypted on application startup
[\#5980](https://github.com/matrix-org/matrix-react-sdk/pull/5980)
* Linkify topics in space room directory results
[\#6015](https://github.com/matrix-org/matrix-react-sdk/pull/6015)
* Persistent space collapsed states
[\#5972](https://github.com/matrix-org/matrix-react-sdk/pull/5972)
* Catch another instance of unlabeled avatars.
[\#6010](https://github.com/matrix-org/matrix-react-sdk/pull/6010)
* Rescale and smooth voice message playback waveform to better match
expectation
[\#5996](https://github.com/matrix-org/matrix-react-sdk/pull/5996)
* Scale voice message clock with user's font size
[\#5993](https://github.com/matrix-org/matrix-react-sdk/pull/5993)
* Remove "in development" flag from voice messages
[\#5995](https://github.com/matrix-org/matrix-react-sdk/pull/5995)
* Support voice messages on Safari
[\#5989](https://github.com/matrix-org/matrix-react-sdk/pull/5989)
* Translations update from Weblate
[\#6011](https://github.com/matrix-org/matrix-react-sdk/pull/6011)
Changes in [3.21.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.21.0) (2021-05-17)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.21.0-rc.1...v3.21.0)
## Security notice
matrix-react-sdk 3.21.0 fixes a low severity issue (GHSA-8796-gc9j-63rv)
related to file upload. When uploading a file, the local file preview can lead
to execution of scripts embedded in the uploaded file, but only after several
user interactions to open the preview in a separate tab. This only impacts the
local user while in the process of uploading. It cannot be exploited remotely
or by other users. Thanks to [Muhammad Zaid Ghifari](https://github.com/MR-ZHEEV)
for responsibly disclosing this via Matrix's Security Disclosure Policy.
## All changes
* Upgrade to JS SDK 11.0.0
* [Release] Add missing space on beta feedback dialog
[\#6019](https://github.com/matrix-org/matrix-react-sdk/pull/6019)
* [Release] Add feedback mechanism for beta features, namely Spaces
[\#6013](https://github.com/matrix-org/matrix-react-sdk/pull/6013)
* Add feedback mechanism for beta features, namely Spaces
[\#6012](https://github.com/matrix-org/matrix-react-sdk/pull/6012)
Changes in [3.21.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.21.0-rc.1) (2021-05-11)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.20.0...v3.21.0-rc.1)
* Upgrade to JS SDK 11.0.0-rc.1
* Add disclaimer about subspaces being experimental in add existing dialog
[\#5978](https://github.com/matrix-org/matrix-react-sdk/pull/5978)
* Spaces Beta release
[\#5933](https://github.com/matrix-org/matrix-react-sdk/pull/5933)
* Improve permissions error when adding new server to room directory
[\#6009](https://github.com/matrix-org/matrix-react-sdk/pull/6009)
* Allow user to progress through space creation & setup using Enter
[\#6006](https://github.com/matrix-org/matrix-react-sdk/pull/6006)
* Upgrade sanitize types
[\#6008](https://github.com/matrix-org/matrix-react-sdk/pull/6008)
* Upgrade `cheerio` and resolve type errors
[\#6007](https://github.com/matrix-org/matrix-react-sdk/pull/6007)
* Add slash commands support to edit message composer
[\#5865](https://github.com/matrix-org/matrix-react-sdk/pull/5865)
* Fix the two todays problem
[\#5940](https://github.com/matrix-org/matrix-react-sdk/pull/5940)
* Switch the Home Space out for an All rooms space
[\#5969](https://github.com/matrix-org/matrix-react-sdk/pull/5969)
* Show device ID in UserInfo when there is no device name
[\#5985](https://github.com/matrix-org/matrix-react-sdk/pull/5985)
* Switch back to release version of `sanitize-html`
[\#6005](https://github.com/matrix-org/matrix-react-sdk/pull/6005)
* Bump hosted-git-info from 2.8.8 to 2.8.9
[\#5998](https://github.com/matrix-org/matrix-react-sdk/pull/5998)
* Don't use the event's metadata to calc the scale of an image
[\#5982](https://github.com/matrix-org/matrix-react-sdk/pull/5982)
* Adjust MIME type of upload confirmation if needed
[\#5981](https://github.com/matrix-org/matrix-react-sdk/pull/5981)
* Forbid redaction of encryption events
[\#5991](https://github.com/matrix-org/matrix-react-sdk/pull/5991)
* Fix voice message playback being squished up against send button
[\#5988](https://github.com/matrix-org/matrix-react-sdk/pull/5988)
* Improve style of notification badges on the space panel
[\#5983](https://github.com/matrix-org/matrix-react-sdk/pull/5983)
* Add dev dependency for parse5 typings
[\#5990](https://github.com/matrix-org/matrix-react-sdk/pull/5990)
* Iterate Spaces admin UX around room management
[\#5977](https://github.com/matrix-org/matrix-react-sdk/pull/5977)
* Guard all isSpaceRoom calls behind the labs flag
[\#5979](https://github.com/matrix-org/matrix-react-sdk/pull/5979)
* Bump lodash from 4.17.20 to 4.17.21
[\#5986](https://github.com/matrix-org/matrix-react-sdk/pull/5986)
* Bump lodash from 4.17.19 to 4.17.21 in /test/end-to-end-tests
[\#5987](https://github.com/matrix-org/matrix-react-sdk/pull/5987)
* Bump ua-parser-js from 0.7.23 to 0.7.28
[\#5984](https://github.com/matrix-org/matrix-react-sdk/pull/5984)
* Update visual style of plain files in the timeline
[\#5971](https://github.com/matrix-org/matrix-react-sdk/pull/5971)
* Support for multiple streams (not MSC3077)
[\#5833](https://github.com/matrix-org/matrix-react-sdk/pull/5833)
* Update space ordering behaviour to match updates in MSC
[\#5963](https://github.com/matrix-org/matrix-react-sdk/pull/5963)
* Improve performance of search all spaces and space switching
[\#5976](https://github.com/matrix-org/matrix-react-sdk/pull/5976)
* Update colours and sizing for voice messages
[\#5970](https://github.com/matrix-org/matrix-react-sdk/pull/5970)
* Update link to Android SDK
[\#5973](https://github.com/matrix-org/matrix-react-sdk/pull/5973)
* Add cleanup functions for image view
[\#5962](https://github.com/matrix-org/matrix-react-sdk/pull/5962)
* Add a note about sharing your IP in P2P calls
[\#5961](https://github.com/matrix-org/matrix-react-sdk/pull/5961)
* Only aggregate DM notifications on the Space Panel in the Home Space
[\#5968](https://github.com/matrix-org/matrix-react-sdk/pull/5968)
* Add retry mechanism and progress bar to add existing to space dialog
[\#5975](https://github.com/matrix-org/matrix-react-sdk/pull/5975)
* Warn on access token reveal
[\#5755](https://github.com/matrix-org/matrix-react-sdk/pull/5755)
* Fix newly joined room appearing under the wrong space
[\#5945](https://github.com/matrix-org/matrix-react-sdk/pull/5945)
* Early rendering for voice messages in the timeline
[\#5955](https://github.com/matrix-org/matrix-react-sdk/pull/5955)
* Calculate the real waveform in the Playback class for voice messages
[\#5956](https://github.com/matrix-org/matrix-react-sdk/pull/5956)
* Don't recurse on arrayFastResample
[\#5957](https://github.com/matrix-org/matrix-react-sdk/pull/5957)
* Support a dark theme for voice messages
[\#5958](https://github.com/matrix-org/matrix-react-sdk/pull/5958)
* Handle no/blocked microphones in voice messages
[\#5959](https://github.com/matrix-org/matrix-react-sdk/pull/5959)
Changes in [3.20.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.20.0) (2021-05-10)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.20.0-rc.1...v3.20.0)
* Upgrade to JS SDK 10.1.0
* [Release] Don't use the event's metadata to calc the scale of an image
[\#6004](https://github.com/matrix-org/matrix-react-sdk/pull/6004)
Changes in [3.20.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.20.0-rc.1) (2021-05-04)
===============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.19.0...v3.20.0-rc.1)
* Upgrade to JS SDK 10.1.0-rc.1
* Translations update from Weblate
[\#5966](https://github.com/matrix-org/matrix-react-sdk/pull/5966)
* Fix more space panel layout and hover behaviour issues
[\#5965](https://github.com/matrix-org/matrix-react-sdk/pull/5965)
* Fix edge case with space panel alignment with subspaces on ff
[\#5964](https://github.com/matrix-org/matrix-react-sdk/pull/5964)
* Fix saving room pill part to history
[\#5951](https://github.com/matrix-org/matrix-react-sdk/pull/5951)
* Generate room preview even when minimized
[\#5948](https://github.com/matrix-org/matrix-react-sdk/pull/5948)
* Another change from recovery passphrase to Security Phrase
[\#5934](https://github.com/matrix-org/matrix-react-sdk/pull/5934)
* Sort rooms in the add existing to space dialog based on recency
[\#5943](https://github.com/matrix-org/matrix-react-sdk/pull/5943)
* Inhibit sending RR when context switching to a room
[\#5944](https://github.com/matrix-org/matrix-react-sdk/pull/5944)
* Prevent room list keyboard handling from landing focus on hidden nodes
[\#5950](https://github.com/matrix-org/matrix-react-sdk/pull/5950)
* Make the text filter search all spaces instead of just the selected one
[\#5942](https://github.com/matrix-org/matrix-react-sdk/pull/5942)
* Enable indent rule and fix indent
[\#5931](https://github.com/matrix-org/matrix-react-sdk/pull/5931)
* Prevent peeking members from reacting
[\#5946](https://github.com/matrix-org/matrix-react-sdk/pull/5946)
* Disallow inline display maths
[\#5939](https://github.com/matrix-org/matrix-react-sdk/pull/5939)
* Space creation prompt user to add existing rooms for "Just Me" spaces
[\#5923](https://github.com/matrix-org/matrix-react-sdk/pull/5923)
* Add test coverage collection script
[\#5937](https://github.com/matrix-org/matrix-react-sdk/pull/5937)
* Fix joining room using via servers regression
[\#5936](https://github.com/matrix-org/matrix-react-sdk/pull/5936)
* Revert "Fixes the two Todays problem in Redaction"
[\#5938](https://github.com/matrix-org/matrix-react-sdk/pull/5938)
* Handle encoded matrix URLs
[\#5903](https://github.com/matrix-org/matrix-react-sdk/pull/5903)
* Render ignored users setting regardless of if there are any
[\#5860](https://github.com/matrix-org/matrix-react-sdk/pull/5860)
* Fix inserting trailing colon after mention/pill
[\#5830](https://github.com/matrix-org/matrix-react-sdk/pull/5830)
* Fixes the two Todays problem in Redaction
[\#5917](https://github.com/matrix-org/matrix-react-sdk/pull/5917)
* Fix page up/down scrolling only half a page
[\#5920](https://github.com/matrix-org/matrix-react-sdk/pull/5920)
* Voice messages: Composer controls
[\#5935](https://github.com/matrix-org/matrix-react-sdk/pull/5935)
* Support MSC3086 asserted identity
[\#5886](https://github.com/matrix-org/matrix-react-sdk/pull/5886)
* Handle possible edge case with getting stuck in "unsent messages" bar
[\#5930](https://github.com/matrix-org/matrix-react-sdk/pull/5930)
* Fix suggested rooms not showing up regression from room list optimisation
[\#5932](https://github.com/matrix-org/matrix-react-sdk/pull/5932)
* Broadcast language change to ElectronPlatform
[\#5913](https://github.com/matrix-org/matrix-react-sdk/pull/5913)
* Fix VoIP PIP frame color
[\#5701](https://github.com/matrix-org/matrix-react-sdk/pull/5701)
* Convert some Flow-typed files to TypeScript
[\#5912](https://github.com/matrix-org/matrix-react-sdk/pull/5912)
* Initial SpaceStore tests work
[\#5906](https://github.com/matrix-org/matrix-react-sdk/pull/5906)
* Fix issues with space hierarchy in layout and with incompatible servers
[\#5926](https://github.com/matrix-org/matrix-react-sdk/pull/5926)
* Scale all mxc thumbs using device pixel ratio for hidpi
[\#5928](https://github.com/matrix-org/matrix-react-sdk/pull/5928)
* Fix add existing to space dialog no longer showing rooms for public spaces
[\#5918](https://github.com/matrix-org/matrix-react-sdk/pull/5918)
* Disable spaces context switching for when exploring a space
[\#5924](https://github.com/matrix-org/matrix-react-sdk/pull/5924)
* Autofocus search box in the add existing to space dialog
[\#5921](https://github.com/matrix-org/matrix-react-sdk/pull/5921)
* Use label element in add existing to space dialog for easier hit target
[\#5922](https://github.com/matrix-org/matrix-react-sdk/pull/5922)
* Dynamic max and min zoom in the new ImageView
[\#5916](https://github.com/matrix-org/matrix-react-sdk/pull/5916)
* Improve message error states
[\#5897](https://github.com/matrix-org/matrix-react-sdk/pull/5897)
* Check for null room in `VisibilityProvider`
[\#5914](https://github.com/matrix-org/matrix-react-sdk/pull/5914)
* Add unit tests for various collection-based utility functions
[\#5910](https://github.com/matrix-org/matrix-react-sdk/pull/5910)
* Spaces visual fixes
[\#5909](https://github.com/matrix-org/matrix-react-sdk/pull/5909)
* Remove reliance on DOM API to generated message preview
[\#5908](https://github.com/matrix-org/matrix-react-sdk/pull/5908)
* Expand upon voice message event & include overall waveform
[\#5888](https://github.com/matrix-org/matrix-react-sdk/pull/5888)
* Use floats for image background opacity
[\#5905](https://github.com/matrix-org/matrix-react-sdk/pull/5905)
* Show invites to spaces at the top of the space panel
[\#5902](https://github.com/matrix-org/matrix-react-sdk/pull/5902)
* Improve edge cases with spaces context switching
[\#5899](https://github.com/matrix-org/matrix-react-sdk/pull/5899)
* Fix spaces notification dots wrongly including upgraded (hidden) rooms
[\#5900](https://github.com/matrix-org/matrix-react-sdk/pull/5900)
* Iterate the spaces face pile design
[\#5898](https://github.com/matrix-org/matrix-react-sdk/pull/5898)
* Fix alignment issue with nested spaces being cut off wrong
[\#5890](https://github.com/matrix-org/matrix-react-sdk/pull/5890)
Changes in [3.19.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.19.0) (2021-04-26)
=====================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.19.0-rc.1...v3.19.0)

View file

@ -28,7 +28,7 @@ Platform Targets:
* WebRTC features (VoIP and Video calling) are only available in Chrome & Firefox.
* Mobile Web is not currently a target platform - instead please use the native
iOS (https://github.com/matrix-org/matrix-ios-kit) and Android
(https://github.com/matrix-org/matrix-android-sdk) SDKs.
(https://github.com/matrix-org/matrix-android-sdk2) SDKs.
All code lands on the `develop` branch - `master` is only used for stable releases.
**Please file PRs against `develop`!!**

2
__mocks__/empty.js Normal file
View file

@ -0,0 +1,2 @@
// Yes, this is empty.
module.exports = {};

View file

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "3.19.0",
"version": "3.23.0",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@ -55,10 +55,9 @@
"dependencies": {
"@babel/runtime": "^7.12.5",
"await-lock": "^2.1.0",
"blueimp-canvas-to-blob": "^3.28.0",
"browser-encrypt-attachment": "^0.3.0",
"browser-request": "^0.3.3",
"cheerio": "^1.0.0-rc.5",
"cheerio": "^1.0.0-rc.9",
"classnames": "^2.2.6",
"commonmark": "^0.29.3",
"counterpart": "^0.18.6",
@ -80,7 +79,7 @@
"linkifyjs": "^2.1.9",
"lodash": "^4.17.20",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-widget-api": "^0.1.0-beta.13",
"matrix-widget-api": "^0.1.0-beta.14",
"minimist": "^1.2.5",
"opus-recorder": "^8.0.3",
"pako": "^2.0.3",
@ -88,18 +87,16 @@
"png-chunks-extract": "^1.0.0",
"prop-types": "^15.7.2",
"qrcode": "^1.4.4",
"qs": "^6.9.6",
"re-resizable": "^6.9.0",
"react": "^16.14.0",
"react": "^17.0.2",
"react-beautiful-dnd": "^4.0.1",
"react-dom": "^16.14.0",
"react-dom": "^17.0.2",
"react-focus-lock": "^2.5.0",
"react-transition-group": "^4.4.1",
"resize-observer-polyfill": "^1.5.1",
"rfc4648": "^1.4.0",
"sanitize-html": "github:apostrophecms/sanitize-html#3c7f93f2058f696f5359e3e58d464161647226db",
"sanitize-html": "^2.3.2",
"tar-js": "^0.3.0",
"text-encoding-utf-8": "^1.0.2",
"url": "^0.11.0",
"what-input": "^5.2.10",
"zxcvbn": "^4.4.2"
@ -121,6 +118,7 @@
"@babel/preset-typescript": "^7.12.7",
"@babel/register": "^7.12.10",
"@babel/traverse": "^7.12.12",
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
"@peculiar/webcrypto": "^1.1.4",
"@sinonjs/fake-timers": "^7.0.2",
"@types/classnames": "^2.2.11",
@ -132,11 +130,12 @@
"@types/modernizr": "^3.5.3",
"@types/node": "^14.14.22",
"@types/pako": "^1.0.1",
"@types/parse5": "^6.0.0",
"@types/qrcode": "^1.3.5",
"@types/react": "^16.9",
"@types/react-dom": "^16.9.10",
"@types/react-transition-group": "^4.4.0",
"@types/sanitize-html": "^1.27.0",
"@types/sanitize-html": "^2.3.1",
"@types/zxcvbn": "^4.4.0",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
@ -145,7 +144,7 @@
"chokidar": "^3.5.1",
"concurrently": "^5.3.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.6",
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.1",
"eslint": "7.18.0",
"eslint-config-matrix-org": "^0.2.0",
"eslint-plugin-babel": "^5.3.1",
@ -158,10 +157,9 @@
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-fetch-mock": "^3.0.3",
"matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2",
"matrix-react-test-utils": "^0.2.3",
"matrix-web-i18n": "github:matrix-org/matrix-web-i18n",
"olm": "https://packages.matrix.org/npm/olm/olm-3.2.1.tgz",
"react-test-renderer": "^16.14.0",
"react-test-renderer": "^17.0.2",
"rimraf": "^3.0.2",
"stylelint": "^13.9.0",
"stylelint-config-standard": "^20.0.0",
@ -185,7 +183,10 @@
],
"moduleNameMapper": {
"\\.(gif|png|svg|ttf|woff2)$": "<rootDir>/__mocks__/imageMock.js",
"\\$webapp/i18n/languages.json": "<rootDir>/__mocks__/languages.json"
"\\$webapp/i18n/languages.json": "<rootDir>/__mocks__/languages.json",
"decoderWorker\\.min\\.js": "<rootDir>/__mocks__/empty.js",
"decoderWorker\\.min\\.wasm": "<rootDir>/__mocks__/empty.js",
"waveWorker\\.min\\.js": "<rootDir>/__mocks__/empty.js"
},
"transformIgnorePatterns": [
"/node_modules/(?!matrix-js-sdk).+$"

View file

@ -45,6 +45,8 @@ html {
N.B. Breaks things when we have legitimate horizontal overscroll */
height: 100%;
overflow: hidden;
// Stop similar overscroll bounce in Firefox Nightly for macOS
overscroll-behavior: none;
}
body {
@ -289,6 +291,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
.mx_Dialog_staticWrapper .mx_Dialog {
z-index: 4010;
contain: content;
}
.mx_Dialog_background {

View file

@ -54,6 +54,7 @@
@import "./views/avatars/_MemberStatusMessageAvatar.scss";
@import "./views/avatars/_PulsedAvatar.scss";
@import "./views/avatars/_WidgetAvatar.scss";
@import "./views/beta/_BetaCard.scss";
@import "./views/context_menus/_CallContextMenu.scss";
@import "./views/context_menus/_IconizedContextMenu.scss";
@import "./views/context_menus/_MessageContextMenu.scss";
@ -62,6 +63,7 @@
@import "./views/dialogs/_AddExistingToSpaceDialog.scss";
@import "./views/dialogs/_AddressPickerDialog.scss";
@import "./views/dialogs/_Analytics.scss";
@import "./views/dialogs/_BetaFeedbackDialog.scss";
@import "./views/dialogs/_BugReportDialog.scss";
@import "./views/dialogs/_ChangelogDialog.scss";
@import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss";
@ -74,6 +76,7 @@
@import "./views/dialogs/_DevtoolsDialog.scss";
@import "./views/dialogs/_EditCommunityPrototypeDialog.scss";
@import "./views/dialogs/_FeedbackDialog.scss";
@import "./views/dialogs/_ForwardDialog.scss";
@import "./views/dialogs/_GroupAddressPicker.scss";
@import "./views/dialogs/_HostSignupDialog.scss";
@import "./views/dialogs/_IncomingSasDialog.scss";
@ -96,6 +99,7 @@
@import "./views/dialogs/_SpaceSettingsDialog.scss";
@import "./views/dialogs/_TabbedIntegrationManagerDialog.scss";
@import "./views/dialogs/_TermsDialog.scss";
@import "./views/dialogs/_UntrustedDeviceDialog.scss";
@import "./views/dialogs/_UploadConfirmDialog.scss";
@import "./views/dialogs/_UserSettingsDialog.scss";
@import "./views/dialogs/_WidgetCapabilitiesPromptDialog.scss";
@ -161,6 +165,7 @@
@import "./views/messages/_MStickerBody.scss";
@import "./views/messages/_MTextBody.scss";
@import "./views/messages/_MVideoBody.scss";
@import "./views/messages/_MVoiceMessageBody.scss";
@import "./views/messages/_MessageActionBar.scss";
@import "./views/messages/_MessageTimestamp.scss";
@import "./views/messages/_MjolnirBody.scss";
@ -175,6 +180,7 @@
@import "./views/messages/_common_CryptoEvent.scss";
@import "./views/right_panel/_BaseCard.scss";
@import "./views/right_panel/_EncryptionInfo.scss";
@import "./views/right_panel/_PinnedMessagesCard.scss";
@import "./views/right_panel/_RoomSummaryCard.scss";
@import "./views/right_panel/_UserInfo.scss";
@import "./views/right_panel/_VerificationPanel.scss";
@ -199,7 +205,6 @@
@import "./views/rooms/_NewRoomIntro.scss";
@import "./views/rooms/_NotificationBadge.scss";
@import "./views/rooms/_PinnedEventTile.scss";
@import "./views/rooms/_PinnedEventsPanel.scss";
@import "./views/rooms/_PresenceLabel.scss";
@import "./views/rooms/_ReplyPreview.scss";
@import "./views/rooms/_RoomBreadcrumbs.scss";
@ -236,6 +241,7 @@
@import "./views/settings/tabs/user/_AppearanceUserSettingsTab.scss";
@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss";
@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss";
@import "./views/settings/tabs/user/_LabsUserSettingsTab.scss";
@import "./views/settings/tabs/user/_MjolnirUserSettingsTab.scss";
@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss";
@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss";

View file

@ -38,6 +38,7 @@ limitations under the License.
position: absolute;
font-size: $font-14px;
z-index: 5001;
contain: content;
}
.mx_ContextualMenu_right {
@ -115,8 +116,3 @@ limitations under the License.
border-top: 8px solid $menu-bg-color;
border-right: 8px solid transparent;
}
.mx_ContextualMenu_spinner {
display: block;
margin: 0 auto;
}

View file

@ -56,6 +56,12 @@ limitations under the License.
.mx_GroupFilterPanel .mx_TagTile {
// opacity: 0.5;
position: relative;
.mx_BetaDot {
position: absolute;
right: -13px;
top: -11px;
}
}
.mx_GroupFilterPanel .mx_TagTile.mx_TagTile_prototype {

View file

@ -25,6 +25,7 @@ $roomListCollapsedWidth: 68px;
// Create a row-based flexbox for the GroupFilterPanel and the room list
display: flex;
contain: content;
.mx_LeftPanel_GroupFilterPanelContainer {
flex-grow: 0;
@ -70,6 +71,7 @@ $roomListCollapsedWidth: 68px;
// aligned correctly. This is also a row-based flexbox.
display: flex;
align-items: center;
contain: content;
&.mx_IndicatorScrollbar_leftOverflow {
mask-image: linear-gradient(90deg, transparent, black 5%);

View file

@ -17,6 +17,11 @@ limitations under the License.
.mx_MyGroups {
display: flex;
flex-direction: column;
.mx_BetaCard {
margin: 0 72px;
max-width: 760px;
}
}
.mx_MyGroups .mx_RoomHeader_simpleHeader {
@ -30,7 +35,7 @@ limitations under the License.
flex-wrap: wrap;
}
.mx_MyGroups > :not(.mx_RoomHeader) {
.mx_MyGroups > :not(.mx_RoomHeader):not(.mx_BetaCard) {
max-width: 960px;
margin: 40px;
}

View file

@ -82,7 +82,6 @@ limitations under the License.
color: $primary-fg-color;
font-size: $font-12px;
display: inline;
padding-left: 0px;
}
.mx_NotificationPanel .mx_EventTile_senderDetails {
@ -103,6 +102,7 @@ limitations under the License.
visibility: visible;
position: initial;
display: inline;
padding-left: 5px;
}
.mx_NotificationPanel .mx_EventTile_line {

View file

@ -25,6 +25,7 @@ limitations under the License.
padding: 4px 0;
box-sizing: border-box;
height: 100%;
contain: strict;
.mx_RoomView_MessageList {
padding: 14px 18px; // top and bottom is 4px smaller to balance with the padding set above
@ -98,6 +99,48 @@ limitations under the License.
mask-position: center;
}
$dot-size: 8px;
$pulse-color: $pinned-unread-color;
.mx_RightPanel_pinnedMessagesButton {
&::before {
mask-image: url('$(res)/img/element-icons/room/pin.svg');
mask-position: center;
}
.mx_RightPanel_pinnedMessagesButton_unreadIndicator {
position: absolute;
right: 0;
top: 0;
margin: 4px;
width: $dot-size;
height: $dot-size;
border-radius: 50%;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_RightPanel_indicator_pulse 2s infinite;
animation-iteration-count: 1;
}
}
@keyframes mx_RightPanel_indicator_pulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
}
100% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0);
}
}
.mx_RightPanel_headerButton_highlight {
&::before {
background-color: $accent-color !important;

View file

@ -61,6 +61,39 @@ limitations under the License.
.mx_RoomDirectory_tableWrapper {
overflow-y: auto;
flex: 1 1 0;
.mx_RoomDirectory_footer {
margin-top: 24px;
text-align: center;
> h5 {
margin: 0;
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-18px;
color: $primary-fg-color;
}
> p {
margin: 40px auto 60px;
font-size: $font-14px;
line-height: $font-20px;
color: $secondary-fg-color;
max-width: 464px; // easier reading
}
> hr {
margin: 0;
border: none;
height: 1px;
background-color: $header-panel-bg-color;
}
.mx_RoomDirectory_newRoom {
margin: 24px auto 0;
width: max-content;
}
}
}
.mx_RoomDirectory_table {
@ -138,11 +171,6 @@ limitations under the License.
color: $settings-grey-fg-color;
}
.mx_RoomDirectory_table tr {
padding-bottom: 10px;
cursor: pointer;
}
.mx_RoomDirectory .mx_RoomView_MessageList {
padding: 0;
}

View file

@ -152,6 +152,7 @@ limitations under the License.
flex: 1;
display: flex;
flex-direction: column;
contain: content;
}
.mx_RoomView_statusArea {
@ -237,6 +238,7 @@ hr.mx_RoomView_myReadMarker {
position: relative;
top: -1px;
z-index: 1;
will-change: width;
transition: width 400ms easeinsine 1s, opacity 400ms easeinsine 1s;
width: 99%;
opacity: 1;

View file

@ -21,5 +21,8 @@ limitations under the License.
display: flex;
flex-direction: column;
justify-content: flex-end;
content-visibility: auto;
contain-intrinsic-size: 50px;
}
}

View file

@ -79,6 +79,10 @@ $activeBorderColor: $secondary-fg-color;
.mx_SpaceItem {
display: inline-flex;
flex-flow: wrap;
&.mx_SpaceItem_narrow {
align-self: baseline;
}
}
.mx_SpaceItem.collapsed {
@ -233,7 +237,6 @@ $activeBorderColor: $secondary-fg-color;
.mx_SpacePanel_badgeContainer {
position: absolute;
height: 16px;
// Create a flexbox to make aligning dot badges easier
display: flex;
@ -245,23 +248,37 @@ $activeBorderColor: $secondary-fg-color;
.mx_NotificationBadge_dot {
// make the smaller dot occupy the same width for centering
margin-left: 7px;
margin-right: 7px;
margin: 0 7px;
}
}
&.collapsed {
.mx_SpaceButton {
.mx_SpacePanel_badgeContainer {
right: -3px;
top: -3px;
right: 0;
top: 0;
.mx_NotificationBadge {
background-clip: padding-box;
}
.mx_NotificationBadge_dot {
margin: 0 -1px 0 0;
border: 3px solid $groupFilterPanel-bg-color;
}
.mx_NotificationBadge_2char,
.mx_NotificationBadge_3char {
margin: -5px -5px 0 0;
border: 2px solid $groupFilterPanel-bg-color;
}
}
&.mx_SpaceButton_active .mx_SpacePanel_badgeContainer {
// when we draw the selection border we move the relative bounds of our parent
// so update our position within the bounds of the parent to maintain position overall
right: -6px;
top: -6px;
right: -3px;
top: -3px;
}
}
}
@ -275,7 +292,7 @@ $activeBorderColor: $secondary-fg-color;
.mx_SpaceButton:hover,
.mx_SpaceButton:focus-within,
.mx_SpaceButton_hasMenuOpen {
&:not(.mx_SpaceButton_home) {
&:not(.mx_SpaceButton_home):not(.mx_SpaceButton_invite) {
// Hide the badge container on hover because it'll be a menu button
.mx_SpacePanel_badgeContainer {
width: 0;

View file

@ -86,7 +86,7 @@ limitations under the License.
color: $primary-fg-color;
.mx_AccessibleButton {
padding: 2px 8px;
padding: 4px 12px;
font-weight: normal;
& + .mx_AccessibleButton {
@ -94,6 +94,11 @@ limitations under the License.
}
}
.mx_AccessibleButton_kind_danger_outline,
.mx_AccessibleButton_kind_primary_outline {
padding: 3px 12px; // to account for the 1px border
}
> span {
margin-left: auto;
}
@ -246,11 +251,17 @@ limitations under the License.
grid-row: 1/3;
.mx_AccessibleButton {
padding: 8px 18px;
line-height: $font-24px;
padding: 4px 16px;
display: inline-block;
visibility: hidden;
}
.mx_AccessibleButton_kind_danger_outline,
.mx_AccessibleButton_kind_primary_outline {
padding: 3px 16px; // to account for the 1px border
}
.mx_Checkbox {
display: inline-flex;
vertical-align: middle;

View file

@ -93,6 +93,10 @@ $SpaceRoomViewInnerWidth: 428px;
}
}
&:not(.mx_SpaceRoomView_landing) .mx_SpaceFeedbackPrompt {
width: $SpaceRoomViewInnerWidth;
}
.mx_SpaceRoomView_buttons {
display: block;
margin-top: 44px;
@ -103,6 +107,10 @@ $SpaceRoomViewInnerWidth: 428px;
padding: 8px 22px;
margin-left: 16px;
}
input.mx_AccessibleButton {
border: none; // override default styles
}
}
.mx_Field {
@ -133,6 +141,44 @@ $SpaceRoomViewInnerWidth: 428px;
box-sizing: border-box;
box-shadow: 2px 15px 30px $dialog-shadow-color;
border-radius: 8px;
position: relative;
// XXX remove this when spaces leaves Beta
.mx_BetaCard_betaPill {
position: absolute;
right: 24px;
top: 32px;
}
// XXX remove this when spaces leaves Beta
.mx_SpaceRoomView_preview_spaceBetaPrompt {
font-weight: $font-semi-bold;
font-size: $font-14px;
line-height: $font-24px;
color: $primary-fg-color;
margin-top: 24px;
position: relative;
padding-left: 24px;
.mx_AccessibleButton_kind_link {
display: inline;
padding: 0;
font-size: inherit;
line-height: inherit;
}
&::before {
content: "";
position: absolute;
height: $font-24px;
width: 20px;
left: 0;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
background-color: $secondary-fg-color;
}
}
.mx_SpaceRoomView_preview_inviter {
display: flex;
@ -238,7 +284,8 @@ $SpaceRoomViewInnerWidth: 428px;
.mx_SpaceRoomView_landing_inviteButton {
position: relative;
padding-left: 40px;
padding: 4px 18px 4px 40px;
line-height: $font-24px;
height: min-content;
&::before {
@ -254,12 +301,35 @@ $SpaceRoomViewInnerWidth: 428px;
mask-image: url('$(res)/img/element-icons/room/invite.svg');
}
}
.mx_SpaceRoomView_landing_settingsButton {
position: relative;
margin-left: 16px;
width: 24px;
height: 24px;
&::before {
position: absolute;
content: "";
left: 0;
top: 0;
height: 24px;
width: 24px;
background: $tertiary-fg-color;
mask-position: center;
mask-size: contain;
mask-repeat: no-repeat;
mask-image: url('$(res)/img/element-icons/settings.svg');
}
}
}
.mx_SpaceRoomView_landing_topic {
font-size: $font-15px;
margin-top: 12px;
margin-bottom: 16px;
white-space: pre-wrap;
word-wrap: break-word;
}
> hr {
@ -268,87 +338,22 @@ $SpaceRoomViewInnerWidth: 428px;
background-color: $groupFilterPanel-bg-color;
}
.mx_SpaceRoomView_landing_adminButtons {
margin-top: 24px;
.mx_AccessibleButton {
position: relative;
width: 160px;
height: 124px;
box-sizing: border-box;
padding: 72px 16px 0;
border-radius: 12px;
border: 1px solid $input-border-color;
margin-right: 28px;
margin-bottom: 20px;
font-size: $font-14px;
display: inline-block;
vertical-align: bottom;
&:last-child {
margin-right: 0;
}
&:hover {
background-color: rgba(141, 151, 165, 0.1);
}
&::before, &::after {
position: absolute;
content: "";
left: 16px;
top: 16px;
height: 40px;
width: 40px;
border-radius: 20px;
}
&::after {
mask-position: center;
mask-size: 30px;
mask-repeat: no-repeat;
background: #ffffff; // white icon fill
}
&.mx_SpaceRoomView_landing_addButton {
&::before {
background-color: #ac3ba8;
}
&::after {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
}
}
&.mx_SpaceRoomView_landing_createButton {
&::before {
background-color: #368bd6;
}
&::after {
mask-image: url('$(res)/img/element-icons/roomlist/explore.svg');
}
}
&.mx_SpaceRoomView_landing_settingsButton {
&::before {
background-color: #5c56f5;
}
&::after {
mask-image: url('$(res)/img/element-icons/settings.svg');
}
}
}
}
.mx_SearchBox {
margin: 0 0 20px;
}
.mx_SpaceFeedbackPrompt {
margin-bottom: 16px;
// hide the HR as we have our own
& + hr {
display: none;
}
}
}
.mx_SpaceRoomView_privateScope {
.mx_AccessibleButton {
> .mx_AccessibleButton {
@mixin SpacePillButton;
}
@ -361,7 +366,63 @@ $SpaceRoomViewInnerWidth: 428px;
}
}
.mx_SpaceRoomView_betaWarning {
padding: 12px 12px 12px 54px;
position: relative;
font-size: $font-15px;
line-height: $font-24px;
width: 432px;
border-radius: 8px;
background-color: $info-plinth-bg-color;
color: $secondary-fg-color;
box-sizing: border-box;
> h3 {
font-weight: $font-semi-bold;
font-size: inherit;
line-height: inherit;
margin: 0;
}
> p {
font-size: inherit;
line-height: inherit;
margin: 0;
}
&::before {
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
content: '';
width: 20px;
height: 20px;
position: absolute;
top: 14px;
left: 14px;
background-color: $secondary-fg-color;
}
}
.mx_SpaceRoomView_inviteTeammates {
// XXX remove this when spaces leaves Beta
.mx_SpaceRoomView_inviteTeammates_betaDisclaimer {
padding: 58px 16px 16px;
position: relative;
border-radius: 8px;
background-color: $header-panel-bg-color;
max-width: $SpaceRoomViewInnerWidth;
margin: 20px 0 30px;
box-sizing: border-box;
.mx_BetaCard_betaPill {
position: absolute;
left: 16px;
top: 16px;
}
}
.mx_SpaceRoomView_inviteTeammates_buttons {
color: $secondary-fg-color;
margin-top: 28px;
@ -443,3 +504,66 @@ $SpaceRoomViewInnerWidth: 428px;
}
}
}
.mx_SpaceFeedbackPrompt {
margin-top: 18px;
margin-bottom: 12px;
> hr {
border: none;
border-top: 1px solid $input-border-color;
margin-bottom: 12px;
}
> div {
display: flex;
flex-direction: row;
font-size: $font-15px;
line-height: $font-24px;
> span {
color: $secondary-fg-color;
position: relative;
padding-left: 32px;
font-size: inherit;
line-height: inherit;
margin-right: auto;
&::before {
content: '';
position: absolute;
left: 0;
top: 2px;
height: 20px;
width: 20px;
background-color: $secondary-fg-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
mask-position: center;
}
}
.mx_AccessibleButton_kind_link {
color: $accent-color;
position: relative;
padding: 0 0 0 24px;
margin-left: 8px;
font-size: inherit;
line-height: inherit;
&::before {
content: '';
position: absolute;
left: 0;
height: 16px;
width: 16px;
background-color: $accent-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/chat-bubbles.svg');
mask-position: center;
}
}
}
}

View file

@ -16,6 +16,7 @@ limitations under the License.
.mx_DecoratedRoomAvatar, .mx_ExtraTile {
position: relative;
contain: content;
&.mx_DecoratedRoomAvatar_cutout .mx_BaseAvatar {
mask-image: url('$(res)/img/element-icons/roomlist/decorated-avatar-mask.svg');

View file

@ -0,0 +1,114 @@
/*
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.
*/
.mx_BetaCard {
margin-bottom: 20px;
padding: 24px;
background-color: $settings-profile-placeholder-bg-color;
border-radius: 8px;
display: flex;
box-sizing: border-box;
> div {
.mx_BetaCard_title {
font-weight: $font-semi-bold;
font-size: $font-18px;
line-height: $font-22px;
color: $primary-fg-color;
margin: 4px 0 14px;
.mx_BetaCard_betaPill {
margin-left: 12px;
}
}
.mx_BetaCard_caption {
font-size: $font-15px;
line-height: $font-20px;
color: $secondary-fg-color;
margin-bottom: 20px;
}
.mx_AccessibleButton {
display: block;
margin: 12px 0;
padding: 7px 40px;
width: auto;
}
.mx_BetaCard_disclaimer {
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
margin-top: 20px;
}
}
> img {
margin: auto 0 auto 20px;
width: 300px;
object-fit: contain;
height: 100%;
}
}
.mx_BetaCard_betaPill {
background-color: $accent-color-alt;
padding: 4px 10px;
border-radius: 8px;
text-transform: uppercase;
font-size: 12px;
line-height: 15px;
color: #FFFFFF;
display: inline-block;
vertical-align: text-bottom;
&.mx_BetaCard_betaPill_clickable {
cursor: pointer;
}
}
$pulse-color: $accent-color-alt;
$dot-size: 12px;
.mx_BetaDot {
border-radius: 50%;
margin: 10px;
height: $dot-size;
width: $dot-size;
transform: scale(1);
background: rgba($pulse-color, 1);
box-shadow: 0 0 0 0 rgba($pulse-color, 1);
animation: mx_Beta_bluePulse 2s infinite;
animation-iteration-count: 20;
}
@keyframes mx_Beta_bluePulse {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0.7);
}
70% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba($pulse-color, 0);
}
100% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba($pulse-color, 0);
}
}

View file

@ -54,7 +54,8 @@ limitations under the License.
display: flex;
margin-top: 12px;
.mx_BaseAvatar {
// we can't target .mx_BaseAvatar here as it'll break the decorated avatar styling
.mx_DecoratedRoomAvatar {
margin-right: 12px;
}
@ -75,10 +76,124 @@ limitations under the License.
}
.mx_AddExistingToSpace_section_spaces {
.mx_BaseAvatar {
margin-right: 12px;
}
.mx_BaseAvatar_image {
border-radius: 8px;
}
}
.mx_AddExistingToSpace_section_experimental {
position: relative;
border-radius: 8px;
margin: 12px 0;
padding: 8px 8px 8px 42px;
background-color: $header-panel-bg-color;
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
&::before {
content: '';
position: absolute;
left: 10px;
top: calc(50% - 8px); // vertical centering
height: 16px;
width: 16px;
background-color: $secondary-fg-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/room/room-summary.svg');
mask-position: center;
}
}
.mx_AddExistingToSpace_footer {
display: flex;
margin-top: 20px;
> span {
flex-grow: 1;
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
.mx_ProgressBar {
height: 8px;
width: 100%;
@mixin ProgressBarBorderRadius 8px;
}
.mx_AddExistingToSpace_progressText {
margin-top: 8px;
font-size: $font-15px;
line-height: $font-24px;
color: $primary-fg-color;
}
> * {
vertical-align: middle;
}
}
.mx_AddExistingToSpace_error {
padding-left: 12px;
> img {
align-self: center;
}
.mx_AddExistingToSpace_errorHeading {
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-18px;
color: $notice-primary-color;
}
.mx_AddExistingToSpace_errorCaption {
margin-top: 4px;
font-size: $font-12px;
line-height: $font-15px;
color: $primary-fg-color;
}
}
.mx_AccessibleButton {
display: inline-block;
align-self: center;
}
.mx_AccessibleButton_kind_primary {
padding: 8px 36px;
}
.mx_AddExistingToSpace_retryButton {
margin-left: 12px;
padding-left: 24px;
position: relative;
&::before {
content: '';
position: absolute;
background-color: $primary-fg-color;
mask-repeat: no-repeat;
mask-position: center;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/retry.svg');
width: 18px;
height: 18px;
left: 0;
}
}
.mx_AccessibleButton_kind_link {
padding: 0;
}
}
}
.mx_AddExistingToSpaceDialog {
@ -101,7 +216,7 @@ limitations under the License.
.mx_BaseAvatar {
display: inline-flex;
margin: 5px 16px 5px 5px;
margin: auto 16px auto 5px;
vertical-align: middle;
}
@ -160,44 +275,7 @@ limitations under the License.
}
}
.mx_AddExistingToSpaceDialog_errorText {
font-weight: $font-semi-bold;
font-size: $font-12px;
line-height: $font-15px;
color: $notice-primary-color;
margin-bottom: 28px;
}
.mx_AddExistingToSpace {
display: contents;
}
.mx_AddExistingToSpaceDialog_footer {
display: flex;
margin-top: 32px;
> span {
flex-grow: 1;
font-size: $font-14px;
line-height: $font-15px;
font-weight: $font-semi-bold;
.mx_AccessibleButton {
font-size: inherit;
display: inline-block;
}
> * {
vertical-align: middle;
}
}
.mx_AccessibleButton {
display: inline-block;
}
.mx_AccessibleButton_kind_link {
padding: 0;
}
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2017 Travis Ralston
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.
@ -14,24 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_PinnedEventsPanel {
border-top: 1px solid $primary-hairline-color;
}
.mx_BetaFeedbackDialog {
.mx_BetaFeedbackDialog_subheading {
color: $primary-fg-color;
font-size: $font-14px;
line-height: $font-20px;
margin-bottom: 24px;
}
.mx_PinnedEventsPanel_body {
max-height: 300px;
overflow-y: auto;
padding-bottom: 15px;
}
.mx_PinnedEventsPanel_header {
margin: 0;
padding-top: 8px;
padding-bottom: 15px;
}
.mx_PinnedEventsPanel_cancel {
margin: 12px;
float: right;
display: inline-block;
.mx_AccessibleButton_kind_link {
padding: 0;
font-size: inherit;
line-height: inherit;
}
}

View file

@ -0,0 +1,159 @@
/*
Copyright 2021 Robin Townsend <robin@robin.town>
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_ForwardDialog {
width: 520px;
color: $primary-fg-color;
display: flex;
flex-direction: column;
flex-wrap: nowrap;
min-height: 0;
height: 80vh;
> h3 {
margin: 0 0 6px;
color: $secondary-fg-color;
font-size: $font-12px;
font-weight: $font-semi-bold;
line-height: $font-15px;
}
> .mx_ForwardDialog_preview {
max-height: 30%;
flex-shrink: 0;
overflow: scroll;
div {
pointer-events: none;
}
.mx_EventTile_msgOption {
display: none;
}
// When forwarding messages from encrypted rooms, EventTile will complain
// that our preview is unencrypted, which doesn't actually matter
.mx_EventTile_e2eIcon_unencrypted {
display: none;
}
// We also hide download links to not encourage users to try interacting
.mx_MFileBody_download {
display: none;
}
}
> hr {
width: 100%;
border: none;
border-top: 1px solid $input-border-color;
margin: 12px 0;
}
> .mx_ForwardList {
display: contents;
.mx_SearchBox {
// To match the space around the title
margin: 0 0 15px 0;
flex-grow: 0;
}
.mx_ForwardList_content {
flex-grow: 1;
}
.mx_ForwardList_noResults {
display: block;
margin-top: 24px;
}
.mx_ForwardList_results {
&:not(:first-child) {
margin-top: 24px;
}
.mx_ForwardList_entry {
display: flex;
justify-content: space-between;
height: 32px;
padding: 6px;
border-radius: 8px;
&:hover {
background-color: $groupFilterPanel-bg-color;
}
.mx_ForwardList_roomButton {
display: flex;
margin-right: 12px;
min-width: 0;
.mx_DecoratedRoomAvatar {
margin-right: 12px;
}
.mx_ForwardList_entry_name {
font-size: $font-15px;
line-height: 30px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: 12px;
}
}
.mx_ForwardList_sendButton {
position: relative;
&:not(.mx_ForwardList_canSend) .mx_ForwardList_sendLabel {
// Hide the "Send" label while preserving button size
visibility: hidden;
}
.mx_ForwardList_sendIcon, .mx_NotificationBadge {
position: absolute;
}
.mx_NotificationBadge {
// Match the failed to send indicator's color with the disabled button
background-color: $button-danger-disabled-fg-color;
}
&.mx_ForwardList_sending .mx_ForwardList_sendIcon {
background-color: $button-primary-bg-color;
mask-image: url('$(res)/img/element-icons/circle-sending.svg');
mask-position: center;
mask-repeat: no-repeat;
mask-size: 14px;
width: 14px;
height: 14px;
}
&.mx_ForwardList_sent .mx_ForwardList_sendIcon {
background-color: $button-primary-bg-color;
mask-image: url('$(res)/img/element-icons/circle-sent.svg');
mask-position: center;
mask-repeat: no-repeat;
mask-size: 14px;
width: 14px;
height: 14px;
}
}
}
}
}
}

View file

@ -17,6 +17,9 @@ limitations under the License.
.mx_InviteDialog_addressBar {
display: flex;
flex-direction: row;
// Right margin for the design. We could apply this to the whole dialog, but then the scrollbar
// for the user section gets weird.
margin: 8px 45px 0 0;
.mx_InviteDialog_editor {
flex: 1;
@ -73,7 +76,7 @@ limitations under the License.
}
.mx_InviteDialog_section {
padding-bottom: 10px;
padding-bottom: 4px;
h3 {
font-size: $font-12px;
@ -82,6 +85,14 @@ limitations under the License.
text-transform: uppercase;
}
> p {
margin: 0;
}
> span {
color: $primary-fg-color;
}
.mx_InviteDialog_subname {
margin-bottom: 10px;
margin-top: -10px; // HACK: Positioning with margins is bad
@ -90,6 +101,63 @@ limitations under the License.
}
}
.mx_InviteDialog_section_hidden_suggestions_disclaimer {
padding: 8px 0 16px 0;
font-size: $font-14px;
> span {
color: $primary-fg-color;
font-weight: 600;
}
> p {
margin: 0;
}
}
.mx_InviteDialog_footer {
border-top: 1px solid $input-border-color;
> h3 {
margin: 12px 0;
font-size: $font-12px;
color: $muted-fg-color;
font-weight: bold;
text-transform: uppercase;
}
.mx_InviteDialog_footer_link {
display: flex;
justify-content: space-between;
border-radius: 4px;
border: solid 1px $light-fg-color;
padding: 8px;
> a {
text-decoration: none;
flex-shrink: 1;
overflow: hidden;
text-overflow: ellipsis;
}
}
.mx_InviteDialog_footer_link_copy {
flex-shrink: 0;
cursor: pointer;
margin-left: 20px;
display: inherit;
> div {
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color;
margin-left: 5px;
width: 20px;
height: 20px;
background-repeat: no-repeat;
}
}
}
.mx_InviteDialog_roomTile {
cursor: pointer;
padding: 5px 10px;
@ -142,6 +210,7 @@ limitations under the License.
.mx_InviteDialog_roomTile_nameStack {
display: inline-block;
overflow: hidden;
}
.mx_InviteDialog_roomTile_name {
@ -157,6 +226,13 @@ limitations under the License.
margin-left: 7px;
}
.mx_InviteDialog_roomTile_name,
.mx_InviteDialog_roomTile_userId {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.mx_InviteDialog_roomTile_time {
text-align: right;
font-size: $font-12px;
@ -212,22 +288,29 @@ limitations under the License.
.mx_InviteDialog {
// Prevent the dialog from jumping around randomly when elements change.
height: 590px;
height: 600px;
padding-left: 20px; // the design wants some padding on the left
display: flex;
flex-direction: column;
.mx_InviteDialog_content {
overflow: hidden;
}
}
.mx_InviteDialog_userSections {
margin-top: 10px;
margin-top: 4px;
overflow-y: auto;
padding-right: 45px;
height: 455px; // mx_InviteDialog's height minus some for the upper elements
padding: 0 45px 4px 0;
height: calc(100% - 115px); // mx_InviteDialog's height minus some for the upper and lower elements
}
// Right margin for the design. We could apply this to the whole dialog, but then the scrollbar
// for the user section gets weird.
.mx_InviteDialog_helpText,
.mx_InviteDialog_addressBar {
margin-right: 45px;
.mx_InviteDialog_hasFooter .mx_InviteDialog_userSections {
height: calc(100% - 175px);
}
.mx_InviteDialog_helpText {
margin: 0;
}
.mx_InviteDialog_helpText .mx_AccessibleButton_kind_link {

View file

@ -50,7 +50,8 @@ limitations under the License.
margin-left: 20px;
display: inherit;
}
.mx_ShareDialog_matrixto_copy > div {
.mx_ShareDialog_matrixto_copy::after {
content: "";
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color;
margin-left: 5px;

View file

@ -1,5 +1,5 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
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.
@ -14,15 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* Automatically focuses the captured reference when receiving a non-null
* object. Useful in scenarios where componentDidMount does not have a
* useful reference to an element, but one needs to focus the element on
* first render. Example usage: ref={focusCapturedRef}
* @param {function} ref The React reference to focus on, if not null
*/
export function focusCapturedRef(ref) {
if (ref) {
ref.focus();
.mx_UntrustedDeviceDialog {
.mx_Dialog_title {
display: flex;
align-items: center;
.mx_E2EIcon {
margin-left: 0;
}
}
}

View file

@ -76,12 +76,16 @@ limitations under the License.
border: 1px solid $button-danger-bg-color;
}
.mx_AccessibleButton_kind_danger.mx_AccessibleButton_disabled,
.mx_AccessibleButton_kind_danger_outline.mx_AccessibleButton_disabled {
.mx_AccessibleButton_kind_danger.mx_AccessibleButton_disabled {
color: $button-danger-disabled-fg-color;
background-color: $button-danger-disabled-bg-color;
}
.mx_AccessibleButton_kind_danger_outline.mx_AccessibleButton_disabled {
color: $button-danger-disabled-bg-color;
border-color: $button-danger-disabled-bg-color;
}
.mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_danger_sm {
padding: 5px 12px;
color: $button-danger-fg-color;

View file

@ -22,6 +22,7 @@ limitations under the License.
}
.mx_ImageView_image_wrapper {
pointer-events: initial;
display: flex;
justify-content: center;
align-items: center;
@ -30,7 +31,6 @@ limitations under the License.
}
.mx_ImageView_image {
pointer-events: all;
flex-shrink: 0;
}
@ -43,7 +43,7 @@ limitations under the License.
}
.mx_ImageView_info_wrapper {
pointer-events: all;
pointer-events: initial;
padding-left: 32px;
display: flex;
flex-direction: row;
@ -63,7 +63,7 @@ limitations under the License.
.mx_ImageView_toolbar {
padding-right: 16px;
pointer-events: all;
pointer-events: initial;
display: flex;
align-items: center;
}

View file

@ -18,7 +18,11 @@ limitations under the License.
display: inline;
}
.mx_InlineSpinner_spin img {
.mx_InlineSpinner img, .mx_InlineSpinner_icon {
margin: 0px 6px;
vertical-align: -3px;
}
.mx_InlineSpinner_icon {
display: inline-block;
}

View file

@ -28,8 +28,7 @@ limitations under the License.
top: 0;
}
&::before, &::after {
content: '';
.mx_MiniAvatarUploader_indicator {
position: absolute;
height: 26px;
@ -37,27 +36,22 @@ limitations under the License.
right: -6px;
bottom: -6px;
}
&::before {
background-color: $primary-bg-color;
border-radius: 50%;
z-index: 1;
}
&::after {
background-color: $secondary-fg-color;
mask-position: center;
mask-repeat: no-repeat;
mask-image: url('$(res)/img/element-icons/camera.svg');
mask-size: 16px;
z-index: 2;
}
.mx_MiniAvatarUploader_cameraIcon {
height: 100%;
width: 100%;
&.mx_MiniAvatarUploader_busy::after {
background: url("$(res)/img/spinner.gif") no-repeat center;
background-size: 80%;
mask: unset;
background-color: $secondary-fg-color;
mask-position: center;
mask-repeat: no-repeat;
mask-image: url('$(res)/img/element-icons/camera.svg');
mask-size: 16px;
z-index: 2;
}
}
}

View file

@ -21,7 +21,7 @@ progress.mx_ProgressBar {
appearance: none;
border: none;
@mixin ProgressBarBorderRadius "6px";
@mixin ProgressBarBorderRadius 6px;
@mixin ProgressBarColour $progressbar-fg-color;
@mixin ProgressBarBgColour $progressbar-bg-color;
::-webkit-progress-value {

View file

@ -26,3 +26,19 @@ limitations under the License.
.mx_MatrixChat_middlePanel .mx_Spinner {
height: auto;
}
@keyframes spin {
from {
transform: rotateZ(0deg);
}
to {
transform: rotateZ(360deg);
}
}
.mx_Spinner_icon {
background-color: $primary-fg-color;
mask: url('$(res)/img/spinner.svg');
mask-size: contain;
animation: 1.1s steps(12, end) infinite spin;
}

View file

@ -61,9 +61,9 @@ limitations under the License.
.mx_MFileBody_info {
background-color: $message-body-panel-bg-color;
border-radius: 4px;
width: 270px;
padding: 8px;
border-radius: 12px;
width: 243px; // same width as a playable voice message, accounting for padding
padding: 6px 12px;
color: $message-body-panel-fg-color;
.mx_MFileBody_info_icon {
@ -82,7 +82,7 @@ limitations under the License.
mask-position: center;
mask-size: cover;
mask-image: url('$(res)/img/element-icons/room/composer/attach.svg');
background-color: $message-body-panel-fg-color;
background-color: $message-body-panel-icon-fg-color;
width: 13px;
height: 15px;

View file

@ -0,0 +1,19 @@
/*
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.
*/
.mx_MVoiceMessageBody {
display: inline-block; // makes the playback controls magically line up
}

View file

@ -20,11 +20,12 @@ limitations under the License.
visibility: hidden;
cursor: pointer;
display: flex;
height: 24px;
height: 32px;
line-height: $font-24px;
border-radius: 4px;
background: $message-action-bar-bg-color;
top: -26px;
border-radius: 8px;
background: $primary-bg-color;
border: 1px solid $input-border-color;
top: -32px;
right: 8px;
user-select: none;
// Ensure the action bar appears above over things, like the read marker.
@ -51,31 +52,19 @@ limitations under the License.
white-space: nowrap;
display: inline-block;
position: relative;
border: 1px solid $message-action-bar-border-color;
margin-left: -1px;
margin: 2px;
&:hover {
border-color: $message-action-bar-hover-border-color;
background: $roomlist-button-bg-color;
border-radius: 6px;
z-index: 1;
}
&:first-child {
border-radius: 3px 0 0 3px;
}
&:last-child {
border-radius: 0 3px 3px 0;
}
&:only-child {
border-radius: 3px;
}
}
}
.mx_MessageActionBar_maskButton {
width: 27px;
width: 28px;
height: 28px;
}
.mx_MessageActionBar_maskButton::after {
@ -85,9 +74,14 @@ limitations under the License.
left: 0;
height: 100%;
width: 100%;
mask-size: 18px;
mask-repeat: no-repeat;
mask-position: center;
background-color: $message-action-bar-fg-color;
background-color: $secondary-fg-color;
}
.mx_MessageActionBar_maskButton:hover::after {
background-color: $primary-fg-color;
}
.mx_MessageActionBar_reactButton::after {

View file

@ -17,18 +17,56 @@ limitations under the License.
.mx_ReactionsRow {
margin: 6px 0;
color: $primary-fg-color;
.mx_ReactionsRow_addReactionButton {
position: relative;
display: inline-block;
visibility: hidden; // show on hover of the .mx_EventTile
width: 24px;
height: 24px;
vertical-align: middle;
margin-left: 4px;
&::before {
content: '';
position: absolute;
height: 100%;
width: 100%;
mask-size: 16px;
mask-repeat: no-repeat;
mask-position: center;
background-color: $tertiary-fg-color;
mask-image: url('$(res)/img/element-icons/room/message-bar/emoji.svg');
}
&.mx_ReactionsRow_addReactionButton_active {
visibility: visible; // keep showing whilst the context menu is shown
}
&:hover, &.mx_ReactionsRow_addReactionButton_active {
&::before {
background-color: $primary-fg-color;
}
}
}
}
.mx_EventTile:hover .mx_ReactionsRow_addReactionButton {
visibility: visible;
}
.mx_ReactionsRow_showAll {
text-decoration: none;
font-size: $font-10px;
font-weight: 600;
margin-left: 6px;
vertical-align: top;
font-size: $font-12px;
line-height: $font-20px;
margin-left: 4px;
vertical-align: middle;
&:hover,
&:link,
&:visited {
color: $accent-color;
&:link, &:visited {
color: $tertiary-fg-color;
}
&:hover {
color: $primary-fg-color;
}
}

View file

@ -16,14 +16,15 @@ limitations under the License.
.mx_ReactionsRowButton {
display: inline-flex;
line-height: $font-21px;
line-height: $font-20px;
margin-right: 6px;
padding: 0 6px;
padding: 1px 6px;
border: 1px solid $reaction-row-button-border-color;
border-radius: 10px;
background-color: $reaction-row-button-bg-color;
cursor: pointer;
user-select: none;
vertical-align: middle;
&:hover {
border-color: $reaction-row-button-hover-border-color;
@ -34,6 +35,10 @@ limitations under the License.
border-color: $reaction-row-button-selected-border-color;
}
&.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
.mx_ReactionsRowButton_content {
max-width: 100px;
overflow: hidden;

View file

@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_SenderProfile_name {
.mx_SenderProfile_displayName {
font-weight: 600;
}
.mx_SenderProfile_mxid {
font-weight: 600;
font-size: 1.1rem;
margin-left: 5px;
opacity: 0.5; // Match mx_TextualEvent
}

View file

@ -0,0 +1,90 @@
/*
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.
*/
.mx_PinnedMessagesCard {
padding-top: 0;
.mx_BaseCard_header {
text-align: center;
margin-top: 0;
border-bottom: 1px solid $menu-border-color;
> h2 {
font-weight: $font-semi-bold;
font-size: $font-18px;
margin: 8px 0;
}
.mx_BaseCard_close {
margin-right: 6px;
}
}
.mx_PinnedMessagesCard_empty {
display: flex;
height: 100%;
> div {
height: max-content;
text-align: center;
margin: auto 40px;
.mx_PinnedMessagesCard_MessageActionBar {
pointer-events: none;
display: flex;
height: 32px;
line-height: $font-24px;
border-radius: 8px;
background: $primary-bg-color;
border: 1px solid $input-border-color;
padding: 1px;
width: max-content;
margin: 0 auto;
box-sizing: border-box;
.mx_MessageActionBar_maskButton {
display: inline-block;
position: relative;
}
.mx_MessageActionBar_optionsButton {
background: $roomlist-button-bg-color;
border-radius: 6px;
z-index: 1;
&::after {
background-color: $primary-fg-color;
}
}
}
> h2 {
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-24px;
color: $primary-fg-color;
margin-top: 24px;
margin-bottom: 20px;
}
> span {
font-size: $font-12px;
line-height: $font-15px;
color: $secondary-fg-color;
}
}
}
}

View file

@ -36,6 +36,7 @@ limitations under the License.
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
white-space: pre-wrap;
}
.mx_RoomSummaryCard_avatar {

View file

@ -16,6 +16,7 @@ limitations under the License.
*/
$left-gutter: 64px;
$hover-select-border: 4px;
.mx_EventTile {
max-width: 100%;
@ -85,12 +86,11 @@ $left-gutter: 64px;
}
.mx_EventTile_isEditing .mx_MessageTimestamp {
visibility: hidden !important;
visibility: hidden;
}
.mx_EventTile .mx_MessageTimestamp {
display: block;
visibility: hidden;
white-space: nowrap;
left: 0px;
text-align: center;
@ -104,7 +104,7 @@ $left-gutter: 64px;
.mx_EventTile_line, .mx_EventTile_reply {
position: relative;
padding-left: $left-gutter;
border-radius: 4px;
border-radius: 8px;
}
.mx_RoomView_timeline_rr_enabled,
@ -142,27 +142,8 @@ $left-gutter: 64px;
line-height: 57px !important;
}
.mx_MessagePanel_alwaysShowTimestamps .mx_MessageTimestamp {
visibility: visible;
}
.mx_EventTile_selected > div > a > .mx_MessageTimestamp {
left: 3px;
width: auto;
}
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)
// The first set is to handle the 'group layout' (default) and the second for the IRC layout
.mx_EventTile_last > div > a > .mx_MessageTimestamp,
.mx_EventTile:hover > div > a > .mx_MessageTimestamp,
.mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp,
.mx_EventTile.focus-visible:focus-within > div > a > .mx_MessageTimestamp,
.mx_IRCLayout .mx_EventTile_last > a > .mx_MessageTimestamp,
.mx_IRCLayout .mx_EventTile:hover > a > .mx_MessageTimestamp,
.mx_IRCLayout .mx_ReplyThread .mx_EventTile > a > .mx_MessageTimestamp,
.mx_IRCLayout .mx_EventTile.mx_EventTile_actionBarFocused > a > .mx_MessageTimestamp,
.mx_IRCLayout .mx_EventTile.focus-visible:focus-within > a > .mx_MessageTimestamp {
visibility: visible;
left: calc(-$hover-select-border);
}
.mx_EventTile:hover .mx_MessageActionBar,
@ -177,7 +158,7 @@ $left-gutter: 64px;
*/
.mx_EventTile_selected > .mx_EventTile_line {
border-left: $accent-color 4px solid;
padding-left: 60px;
padding-left: calc($left-gutter - $hover-select-border);
background-color: $event-selected-color;
}
@ -190,8 +171,12 @@ $left-gutter: 64px;
}
}
.mx_EventTile_info .mx_EventTile_line {
padding-left: calc($left-gutter + 18px);
}
.mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line {
padding-left: 78px;
padding-left: calc($left-gutter + 18px - $hover-select-border);
}
.mx_EventTile:hover .mx_EventTile_line,
@ -280,6 +265,7 @@ $left-gutter: 64px;
height: $font-14px;
width: $font-14px;
will-change: left, top;
transition:
left var(--transition-short) ease-out,
top var(--transition-standard) ease-out;
@ -426,7 +412,7 @@ $left-gutter: 64px;
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line {
padding-left: 60px;
padding-left: calc($left-gutter - $hover-select-border);
}
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line {
@ -444,7 +430,7 @@ $left-gutter: 64px;
.mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line,
.mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line {
padding-left: 78px;
padding-left: calc($left-gutter + 18px - $hover-select-border);
}
/* End to end encryption stuff */
@ -456,7 +442,7 @@ $left-gutter: 64px;
.mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp,
.mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp {
width: $MessageTimestamp_width_hover;
left: calc(-$hover-select-border);
}
// Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies)

View file

@ -24,10 +24,6 @@ $left-gutter: 64px;
margin-left: $left-gutter;
}
> .mx_EventTile_line {
padding-left: $left-gutter;
}
> .mx_EventTile_avatar {
position: absolute;
}
@ -43,10 +39,6 @@ $left-gutter: 64px;
line-height: $font-22px;
}
}
.mx_EventTile_info .mx_EventTile_line {
padding-left: calc($left-gutter + 18px);
}
}
/* Compact layout overrides */

View file

@ -115,8 +115,7 @@ $irc-line-height: $font-18px;
.mx_EventTile_line {
.mx_EventTile_e2eIcon,
.mx_TextualEvent,
.mx_MTextBody,
.mx_ReplyThread_wrapper_empty {
.mx_MTextBody {
display: inline-block;
}
}
@ -177,16 +176,13 @@ $irc-line-height: $font-18px;
.mx_SenderProfile_hover {
background-color: $primary-bg-color;
overflow: hidden;
display: flex;
> span {
display: flex;
> .mx_SenderProfile_name {
overflow: hidden;
text-overflow: ellipsis;
min-width: var(--name-width);
text-align: end;
}
> .mx_SenderProfile_displayName {
overflow: hidden;
text-overflow: ellipsis;
min-width: var(--name-width);
text-align: end;
}
}
@ -211,7 +207,7 @@ $irc-line-height: $font-18px;
background: transparent;
> span {
> .mx_SenderProfile_name {
> .mx_SenderProfile_displayName {
min-width: inherit;
}
}

View file

@ -52,6 +52,7 @@ limitations under the License.
.mx_JumpToBottomButton_scrollDown {
position: relative;
display: block;
height: 38px;
border-radius: 19px;
box-sizing: border-box;

View file

@ -18,8 +18,8 @@ limitations under the License.
margin: 40px 0 48px 64px;
.mx_MiniAvatarUploader_hasAvatar:not(.mx_MiniAvatarUploader_busy):not(:hover) {
&::before, &::after {
content: unset;
.mx_MiniAvatarUploader_indicator {
display: none;
}
}

View file

@ -16,62 +16,91 @@ limitations under the License.
.mx_PinnedEventTile {
min-height: 40px;
margin-bottom: 5px;
width: 100%;
border-radius: 5px; // for the hover
}
padding: 0 4px 12px;
.mx_PinnedEventTile:hover {
background-color: $event-selected-color;
}
display: grid;
grid-template-areas:
"avatar name remove"
"content content content"
"footer footer footer";
grid-template-rows: max-content auto max-content;
grid-template-columns: 24px auto 24px;
grid-row-gap: 12px;
grid-column-gap: 8px;
.mx_PinnedEventTile .mx_PinnedEventTile_sender,
.mx_PinnedEventTile .mx_PinnedEventTile_timestamp {
color: #868686;
font-size: 0.8em;
vertical-align: top;
display: inline-block;
padding-bottom: 3px;
}
& + .mx_PinnedEventTile {
padding: 12px 4px;
border-top: 1px solid $menu-border-color;
}
.mx_PinnedEventTile .mx_PinnedEventTile_timestamp {
padding-left: 15px;
display: none;
}
.mx_PinnedEventTile_senderAvatar {
grid-area: avatar;
}
.mx_PinnedEventTile .mx_PinnedEventTile_senderAvatar .mx_BaseAvatar {
float: left;
margin-right: 10px;
}
.mx_PinnedEventTile_sender {
grid-area: name;
font-weight: $font-semi-bold;
font-size: $font-15px;
line-height: $font-24px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
.mx_PinnedEventTile_actions {
float: right;
margin-right: 10px;
display: none;
}
.mx_PinnedEventTile_unpinButton {
visibility: hidden;
grid-area: remove;
position: relative;
width: 24px;
height: 24px;
border-radius: 8px;
.mx_PinnedEventTile:hover .mx_PinnedEventTile_timestamp {
display: inline-block;
}
&:hover {
background-color: $roomheader-addroom-bg-color;
}
.mx_PinnedEventTile:hover .mx_PinnedEventTile_actions {
display: block;
}
&::before {
content: "";
position: absolute;
//top: 0;
//left: 0;
height: inherit;
width: inherit;
background: $secondary-fg-color;
mask-position: center;
mask-size: 8px;
mask-repeat: no-repeat;
mask-image: url('$(res)/img/image-view/close.svg');
}
}
.mx_PinnedEventTile_unpinButton {
display: inline-block;
cursor: pointer;
margin-left: 10px;
}
.mx_PinnedEventTile_message {
grid-area: content;
}
.mx_PinnedEventTile_gotoButton {
display: inline-block;
font-size: 0.7em; // Smaller text to avoid conflicting with the layout
}
.mx_PinnedEventTile_footer {
grid-area: footer;
font-size: 10px;
line-height: 12px;
.mx_PinnedEventTile_message {
margin-left: 50px;
position: relative;
top: 0;
left: 0;
.mx_PinnedEventTile_timestamp {
font-size: inherit;
line-height: inherit;
color: $secondary-fg-color;
}
.mx_AccessibleButton_kind_link {
padding: 0;
margin-left: 12px;
font-size: inherit;
line-height: inherit;
}
}
&:hover {
.mx_PinnedEventTile_unpinButton {
visibility: visible;
}
}
}

View file

@ -32,14 +32,14 @@ limitations under the License.
// first triggering the enter state with the newest breadcrumb off screen (-40px) then
// sliding it into view.
&.mx_RoomBreadcrumbs-enter {
margin-left: -40px; // 32px for the avatar, 8px for the margin
transform: translateX(-40px); // 32px for the avatar, 8px for the margin
}
&.mx_RoomBreadcrumbs-enter-active {
margin-left: 0;
transform: translateX(0);
// Timing function is as-requested by design.
// NOTE: The transition time MUST match the value passed to CSSTransition!
transition: margin-left 640ms cubic-bezier(0.66, 0.02, 0.36, 1);
transition: transform 640ms cubic-bezier(0.66, 0.02, 0.36, 1);
}
.mx_RoomBreadcrumbs_placeholder {

View file

@ -277,24 +277,6 @@ limitations under the License.
margin-top: 18px;
}
.mx_RoomHeader_pinnedButton::before {
mask-image: url('$(res)/img/element-icons/room/pin.svg');
}
.mx_RoomHeader_pinsIndicator {
position: absolute;
right: 0;
bottom: 4px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: $pinned-color;
}
.mx_RoomHeader_pinsIndicatorUnread {
background-color: $pinned-unread-color;
}
@media only screen and (max-width: 480px) {
.mx_RoomHeader_wrapper {
padding: 0;

View file

@ -61,8 +61,8 @@ limitations under the License.
&.mx_RoomSublist_headerContainer_sticky {
position: fixed;
height: 32px; // to match the header container
// width set by JS
width: calc(100% - 22px);
// width set by JS because of a compat issue between Firefox and Chrome
width: calc(100% - 15px);
}
// We don't have a top style because the top is dependent on the room list header's
@ -98,7 +98,7 @@ limitations under the License.
position: relative;
width: 24px;
height: 24px;
border-radius: 32px;
border-radius: 8px;
&::before {
content: '';
@ -114,6 +114,11 @@ limitations under the License.
}
}
.mx_RoomSublist_auxButton:hover,
.mx_RoomSublist_menuButton:hover {
background: $roomlist-button-bg-color;
}
// Hide the menu button by default
.mx_RoomSublist_menuButton {
visibility: hidden;
@ -193,6 +198,7 @@ limitations under the License.
// as the box model should be top aligned. Happens in both FF and Chromium
display: flex;
flex-direction: column;
align-self: stretch;
mask-image: linear-gradient(0deg, transparent, black 4px);
}

View file

@ -19,6 +19,10 @@ limitations under the License.
margin-bottom: 4px;
padding: 4px;
contain: content; // Not strict as it will break when resizing a sublist vertically
height: 40px;
box-sizing: border-box;
// The tile is also a flexbox row itself
display: flex;

View file

@ -39,23 +39,25 @@ limitations under the License.
width: 14px; // w&h are size of icon
height: 18px;
vertical-align: middle;
margin-right: 7px; // distance from left edge of waveform container (container has some margin too)
background-color: $muted-fg-color;
margin-right: 11px; // distance from left edge of waveform container (container has some margin too)
background-color: $voice-record-icon-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-image: url('$(res)/img/element-icons/trashcan.svg');
}
.mx_VoiceMessagePrimaryContainer {
.mx_MessageComposer_row .mx_VoiceMessagePrimaryContainer {
// Note: remaining class properties are in the PlayerContainer CSS.
margin: 6px; // force the composer area to put a gutter around us
margin-right: 12px; // isolate from stop button
margin-right: 12px; // isolate from stop/send button
position: relative; // important for the live circle
&.mx_VoiceRecordComposerTile_recording {
padding-left: 16px; // +10px for the live circle, +6px for regular padding
// We are putting the circle in this padding, so we need +10px from the regular
// padding on the left side.
padding-left: 22px;
&::before {
animation: recording-pulse 2s infinite;
@ -65,8 +67,8 @@ limitations under the License.
width: 10px;
height: 10px;
position: absolute;
left: 8px;
top: 16px; // vertically center
left: 12px; // 12px from the left edge for container padding
top: 18px; // vertically center (middle align with clock)
border-radius: 10px;
}
}

View file

@ -22,3 +22,34 @@ limitations under the License.
.mx_HelpUserSettingsTab span.mx_AccessibleButton {
word-break: break-word;
}
.mx_HelpUserSettingsTab code {
word-break: break-all;
user-select: all;
}
.mx_HelpUserSettingsTab_accessToken {
display: flex;
justify-content: space-between;
border-radius: 5px;
border: solid 1px $light-fg-color;
margin-bottom: 10px;
margin-top: 10px;
padding: 10px;
}
.mx_HelpUserSettingsTab_accessToken_copy {
flex-shrink: 0;
cursor: pointer;
margin-left: 20px;
display: inherit;
}
.mx_HelpUserSettingsTab_accessToken_copy > div {
mask-image: url($copy-button-url);
background-color: $message-action-bar-fg-color;
margin-left: 5px;
width: 20px;
height: 20px;
background-repeat: no-repeat;
}

View file

@ -0,0 +1,25 @@
/*
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.
*/
.mx_LabsUserSettingsTab {
.mx_SettingsTab_section {
margin-top: 32px;
.mx_SettingsFlag {
margin-right: 0; // remove right margin to align with beta cards
}
}
}

View file

@ -29,6 +29,7 @@ $spacePanelWidth: 71px;
width: 480px;
box-sizing: border-box;
background-color: $primary-bg-color;
position: relative;
> div {
> h2 {
@ -44,6 +45,13 @@ $spacePanelWidth: 71px;
}
}
// XXX remove this when spaces leaves Beta
.mx_BetaCard_betaPill {
position: absolute;
top: 24px;
right: 24px;
}
.mx_SpaceCreateMenuType {
@mixin SpacePillButton;
}
@ -59,7 +67,7 @@ $spacePanelWidth: 71px;
width: 28px;
height: 28px;
position: relative;
background-color: $theme-button-bg-color;
background-color: $roomlist-button-bg-color;
border-radius: 14px;
margin-bottom: 12px;
@ -70,7 +78,7 @@ $spacePanelWidth: 71px;
width: 28px;
top: 0;
left: 0;
background-color: $muted-fg-color;
background-color: $tertiary-fg-color;
transform: rotate(90deg);
mask-repeat: no-repeat;
mask-position: 2px 3px;

View file

@ -19,12 +19,12 @@ limitations under the License.
width: 32px;
height: 32px;
border-radius: 32px;
background-color: $primary-bg-color;
background-color: $voice-playback-button-bg-color;
&::before {
content: '';
position: absolute; // sizing varies by icon
background-color: $muted-fg-color;
background-color: $voice-playback-button-fg-color;
mask-repeat: no-repeat;
mask-size: contain;
}

View file

@ -19,8 +19,9 @@ limitations under the License.
// Container for live recording and playback controls
.mx_VoiceMessagePrimaryContainer {
padding: 6px; // makes us 4px taller than the send/stop button
padding-right: 5px; // there's 1px from the waveform itself, so account for that
// 7px top and bottom for visual design. 12px left & right, but the waveform (right)
// has a 1px padding on it that we want to account for.
padding: 7px 12px 7px 11px;
background-color: $voice-record-waveform-bg-color;
border-radius: 12px;
@ -30,11 +31,9 @@ limitations under the License.
color: $voice-record-waveform-fg-color;
font-size: $font-14px;
line-height: $font-24px;
.mx_Waveform {
// We want the bars to be 2px shorter than the play/pause button in the waveform control
height: 28px; // default is 30px, so we're subtracting the 2px border off the bars
.mx_Waveform_bar {
background-color: $voice-record-waveform-incomplete-fg-color;
@ -47,8 +46,8 @@ limitations under the License.
}
.mx_Clock {
padding-right: 4px; // isolate from waveform
padding-left: 8px; // isolate from live circle
width: 40px; // we're not using a monospace font, so fake it
width: $font-42px; // we're not using a monospace font, so fake it
padding-right: 6px; // with the fixed width this ends up as a visual 8px most of the time, as intended.
padding-left: 8px; // isolate from recording circle / play control
}
}

View file

@ -64,14 +64,17 @@ limitations under the License.
}
}
.mx_CallView_voice {
.mx_CallView_content {
position: relative;
display: flex;
flex-direction: column;
border-radius: 8px;
}
.mx_CallView_voice {
align-items: center;
justify-content: center;
flex-direction: column;
background-color: $inverted-bg-color;
border-radius: 8px;
}
.mx_CallView_voice_avatarsContainer {
@ -108,9 +111,7 @@ limitations under the License.
.mx_CallView_video {
width: 100%;
height: 100%;
position: relative;
z-index: 30;
border-radius: 8px;
overflow: hidden;
}

View file

@ -14,21 +14,37 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
.mx_VideoFeed_voice {
// We don't want to collide with the call controls that have 52px of height
padding-bottom: 52px;
background-color: $inverted-bg-color;
}
.mx_VideoFeed_remote {
width: 100%;
height: 100%;
background-color: #000;
z-index: 50;
display: flex;
justify-content: center;
align-items: center;
&.mx_VideoFeed_video {
background-color: #000;
}
}
.mx_VideoFeed_local {
width: 25%;
height: 25%;
max-width: 25%;
max-height: 25%;
position: absolute;
right: 10px;
top: 10px;
z-index: 100;
border-radius: 4px;
&.mx_VideoFeed_video {
background-color: transparent;
}
}
.mx_VideoFeed_mirror {

BIN
res/img/betas/spaces.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

View file

@ -1,7 +1,7 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20ZM13.389 11.7659C13.6794 11.3162 14.2793 11.187 14.729 11.4774C15.1788 11.7677 15.308 12.3676 15.0176 12.8174C13.9565 14.461 12.1059 15.5526 9.99965 15.5526C7.89343 15.5526 6.04281 14.461 4.98167 12.8174C4.69133 12.3677 4.82053 11.7677 5.27025 11.4774C5.71997 11.187 6.31991 11.3162 6.61025 11.7659C7.32946 12.88 8.57908 13.6141 9.99965 13.6141C11.4202 13.6141 12.6698 12.88 13.389 11.7659ZM8 8C8 8.82843 7.44036 9.5 6.75 9.5C6.05964 9.5 5.5 8.82843 5.5 8C5.5 7.17157 6.05964 6.5 6.75 6.5C7.44036 6.5 8 7.17157 8 8ZM13.25 9.5C13.9404 9.5 14.5 8.82843 14.5 8C14.5 7.17157 13.9404 6.5 13.25 6.5C12.5596 6.5 12 7.17157 12 8C12 8.82843 12.5596 9.5 13.25 9.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20ZM13.389 11.7659C13.6794 11.3162 14.2793 11.187 14.729 11.4774C15.1788 11.7677 15.308 12.3676 15.0176 12.8174C13.9565 14.461 12.1059 15.5526 9.99965 15.5526C7.89343 15.5526 6.04281 14.461 4.98167 12.8174C4.69133 12.3677 4.82053 11.7677 5.27025 11.4774C5.71997 11.187 6.31991 11.3162 6.61025 11.7659C7.32946 12.88 8.57908 13.6141 9.99965 13.6141C11.4202 13.6141 12.6698 12.88 13.389 11.7659ZM8 8C8 8.82843 7.44036 9.5 6.75 9.5C6.05964 9.5 5.5 8.82843 5.5 8C5.5 7.17157 6.05964 6.5 6.75 6.5C7.44036 6.5 8 7.17157 8 8ZM13.25 9.5C13.9404 9.5 14.5 8.82843 14.5 8C14.5 7.17157 13.9404 6.5 13.25 6.5C12.5596 6.5 12 7.17157 12 8C12 8.82843 12.5596 9.5 13.25 9.5Z" fill="black"/>
<path d="M14.1748 11.4164C14.0254 11.6478 14.0919 11.9565 14.3233 12.1059C14.5546 12.2553 14.8633 12.1888 15.0127 11.9574L14.1748 11.4164ZM15.148 11.7479C15.2974 11.5165 15.2309 11.2078 14.9995 11.0584C14.7681 10.909 14.4595 10.9755 14.3101 11.2069L15.148 11.7479ZM15.0127 11.9574L15.148 11.7479L14.3101 11.2069L14.1748 11.4164L15.0127 11.9574ZM14.729 11.4774L15.27 10.6394L15.27 10.6394L14.729 11.4774ZM13.389 11.7659L12.5511 11.225V11.225L13.389 11.7659ZM15.0176 12.8174L14.1797 12.2764V12.2764L15.0176 12.8174ZM4.98167 12.8174L5.8196 12.2764L5.8196 12.2764L4.98167 12.8174ZM5.27025 11.4774L4.72928 10.6394L4.72928 10.6394L5.27025 11.4774ZM6.61025 11.7659L5.77233 12.3069L6.61025 11.7659ZM19.0026 10C19.0026 14.972 14.972 19.0026 10 19.0026V20.9974C16.0737 20.9974 20.9974 16.0737 20.9974 10H19.0026ZM10 0.997378C14.972 0.997378 19.0026 5.02799 19.0026 10H20.9974C20.9974 3.92632 16.0737 -0.997378 10 -0.997378V0.997378ZM0.997378 10C0.997378 5.02799 5.02799 0.997378 10 0.997378V-0.997378C3.92632 -0.997378 -0.997378 3.92632 -0.997378 10H0.997378ZM10 19.0026C5.02799 19.0026 0.997378 14.972 0.997378 10H-0.997378C-0.997378 16.0737 3.92632 20.9974 10 20.9974V19.0026ZM15.27 10.6394C14.3575 10.0503 13.1402 10.3125 12.5511 11.225L14.227 12.3069C14.2259 12.3086 14.2229 12.312 14.2187 12.315C14.215 12.3174 14.2118 12.3186 14.2092 12.3192C14.2067 12.3197 14.2033 12.32 14.1989 12.3192C14.1939 12.3183 14.1898 12.3164 14.1881 12.3153L15.27 10.6394ZM15.8555 13.3583C16.4447 12.4458 16.1825 11.2286 15.27 10.6394L14.1881 12.3153C14.1863 12.3142 14.1829 12.3113 14.18 12.307C14.1775 12.3033 14.1764 12.3001 14.1758 12.2976C14.1753 12.2951 14.175 12.2917 14.1758 12.2873C14.1767 12.2822 14.1786 12.2781 14.1797 12.2764L15.8555 13.3583ZM9.99965 16.55C12.4589 16.55 14.6186 15.2742 15.8555 13.3583L14.1797 12.2764C13.2943 13.6478 11.7528 14.5552 9.99965 14.5552V16.55ZM4.14375 13.3583C5.38067 15.2742 7.54041 16.55 9.99965 16.55V14.5552C8.24645 14.5552 6.70495 13.6478 5.8196 12.2764L4.14375 13.3583ZM4.72928 10.6394C3.81679 11.2286 3.55464 12.4458 4.14375 13.3583L5.8196 12.2764C5.8207 12.2781 5.82261 12.2822 5.8235 12.2873C5.82426 12.2917 5.824 12.2951 5.82346 12.2976C5.82292 12.3001 5.82175 12.3033 5.81926 12.307C5.81635 12.3113 5.81294 12.3142 5.81122 12.3153L4.72928 10.6394ZM7.44818 11.225C6.85907 10.3125 5.64178 10.0503 4.72928 10.6394L5.81122 12.3153C5.8095 12.3164 5.80542 12.3183 5.80034 12.3192C5.79597 12.32 5.79256 12.3197 5.79004 12.3192C5.78752 12.3186 5.7843 12.3174 5.78064 12.315C5.77637 12.3121 5.77344 12.3086 5.77233 12.3069L7.44818 11.225ZM9.99965 12.6167C8.93153 12.6167 7.99128 12.0662 7.44818 11.225L5.77233 12.3069C6.66764 13.6937 8.22663 14.6115 9.99965 14.6115V12.6167ZM12.5511 11.225C12.008 12.0662 11.0678 12.6167 9.99965 12.6167V14.6115C11.7727 14.6115 13.3316 13.6937 14.227 12.3069L12.5511 11.225ZM6.75 10.4974C8.15374 10.4974 8.99738 9.20127 8.99738 8H7.00262C7.00262 8.19305 6.93691 8.33907 6.86768 8.42215C6.80022 8.50311 6.75428 8.50262 6.75 8.50262V10.4974ZM4.50262 8C4.50262 9.20127 5.34626 10.4974 6.75 10.4974V8.50262C6.74572 8.50262 6.69978 8.50311 6.63232 8.42215C6.56309 8.33907 6.49738 8.19305 6.49738 8H4.50262ZM6.75 5.50262C5.34626 5.50262 4.50262 6.79873 4.50262 8H6.49738C6.49738 7.80695 6.56309 7.66093 6.63232 7.57785C6.69978 7.49689 6.74572 7.49738 6.75 7.49738V5.50262ZM8.99738 8C8.99738 6.79873 8.15374 5.50262 6.75 5.50262V7.49738C6.75428 7.49738 6.80022 7.49689 6.86768 7.57785C6.93691 7.66093 7.00262 7.80695 7.00262 8H8.99738ZM13.5026 8C13.5026 8.19305 13.4369 8.33907 13.3677 8.42215C13.3002 8.50311 13.2543 8.50262 13.25 8.50262V10.4974C14.6537 10.4974 15.4974 9.20127 15.4974 8H13.5026ZM13.25 7.49738C13.2543 7.49738 13.3002 7.49689 13.3677 7.57785C13.4369 7.66093 13.5026 7.80695 13.5026 8H15.4974C15.4974 6.79873 14.6537 5.50262 13.25 5.50262V7.49738ZM12.9974 8C12.9974 7.80695 13.0631 7.66093 13.1323 7.57785C13.1998 7.49689 13.2457 7.49738 13.25 7.49738V5.50262C11.8463 5.50262 11.0026 6.79873 11.0026 8H12.9974ZM13.25 8.50262C13.2457 8.50262 13.1998 8.50311 13.1323 8.42215C13.0631 8.33907 12.9974 8.19305 12.9974 8H11.0026C11.0026 9.20127 11.8463 10.4974 13.25 10.4974V8.50262Z" fill="black" mask="url(#path-1-inside-1)"/>
<mask id="path-1-inside-1" fill="white">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20ZM7.99989 7.5C7.99989 8.33 7.32989 9 6.49989 9C5.66989 9 4.99989 8.33 4.99989 7.5C4.99989 6.67 5.66989 6 6.49989 6C7.32989 6 7.99989 6.67 7.99989 7.5ZM14.9999 7.5C14.9999 8.33 14.3299 9 13.4999 9C12.6699 9 11.9999 8.33 11.9999 7.5C11.9999 6.67 12.6699 6 13.4999 6C14.3299 6 14.9999 6.67 14.9999 7.5ZM9.99989 15.5C12.3299 15.5 14.3099 14.04 15.1099 12H4.88989C5.68989 14.04 7.66989 15.5 9.99989 15.5Z"/>
</mask>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 20C15.5228 20 20 15.5228 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20ZM7.99989 7.5C7.99989 8.33 7.32989 9 6.49989 9C5.66989 9 4.99989 8.33 4.99989 7.5C4.99989 6.67 5.66989 6 6.49989 6C7.32989 6 7.99989 6.67 7.99989 7.5ZM14.9999 7.5C14.9999 8.33 14.3299 9 13.4999 9C12.6699 9 11.9999 8.33 11.9999 7.5C11.9999 6.67 12.6699 6 13.4999 6C14.3299 6 14.9999 6.67 14.9999 7.5ZM9.99989 15.5C12.3299 15.5 14.3099 14.04 15.1099 12H4.88989C5.68989 14.04 7.66989 15.5 9.99989 15.5Z" fill="#737D8C"/>
<path d="M15.1099 12L16.0384 12.3641L16.5724 11.0026H15.1099V12ZM4.88989 12V11.0026H3.42744L3.96136 12.3641L4.88989 12ZM19.0026 10C19.0026 14.972 14.972 19.0026 10 19.0026V20.9974C16.0737 20.9974 20.9974 16.0737 20.9974 10H19.0026ZM10 0.997378C14.972 0.997378 19.0026 5.02799 19.0026 10H20.9974C20.9974 3.92632 16.0737 -0.997378 10 -0.997378V0.997378ZM0.997378 10C0.997378 5.02799 5.02799 0.997378 10 0.997378V-0.997378C3.92632 -0.997378 -0.997378 3.92632 -0.997378 10H0.997378ZM10 19.0026C5.02799 19.0026 0.997378 14.972 0.997378 10H-0.997378C-0.997378 16.0737 3.92632 20.9974 10 20.9974V19.0026ZM6.49989 9.99738C7.88073 9.99738 8.99727 8.88084 8.99727 7.5H7.00251C7.00251 7.77916 6.77906 8.00262 6.49989 8.00262V9.99738ZM4.00251 7.5C4.00251 8.88084 5.11906 9.99738 6.49989 9.99738V8.00262C6.22073 8.00262 5.99727 7.77916 5.99727 7.5H4.00251ZM6.49989 5.00262C5.11906 5.00262 4.00251 6.11916 4.00251 7.5H5.99727C5.99727 7.22084 6.22073 6.99738 6.49989 6.99738V5.00262ZM8.99727 7.5C8.99727 6.11916 7.88073 5.00262 6.49989 5.00262V6.99738C6.77906 6.99738 7.00251 7.22084 7.00251 7.5H8.99727ZM13.4999 9.99738C14.8807 9.99738 15.9973 8.88084 15.9973 7.5H14.0025C14.0025 7.77916 13.7791 8.00262 13.4999 8.00262V9.99738ZM11.0025 7.5C11.0025 8.88084 12.1191 9.99738 13.4999 9.99738V8.00262C13.2207 8.00262 12.9973 7.77916 12.9973 7.5H11.0025ZM13.4999 5.00262C12.1191 5.00262 11.0025 6.11916 11.0025 7.5H12.9973C12.9973 7.22084 13.2207 6.99738 13.4999 6.99738V5.00262ZM15.9973 7.5C15.9973 6.11916 14.8807 5.00262 13.4999 5.00262V6.99738C13.7791 6.99738 14.0025 7.22084 14.0025 7.5H15.9973ZM14.1814 11.6359C13.5241 13.3119 11.9006 14.5026 9.99989 14.5026V16.4974C12.7592 16.4974 15.0957 14.7681 16.0384 12.3641L14.1814 11.6359ZM4.88989 12.9974H15.1099V11.0026H4.88989V12.9974ZM9.99989 14.5026C8.09919 14.5026 6.47569 13.3119 5.81842 11.6359L3.96136 12.3641C4.9041 14.7681 7.24059 16.4974 9.99989 16.4974V14.5026Z" fill="#737D8C" mask="url(#path-1-inside-1)"/>
</svg>

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1,5 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<ellipse cx="5" cy="5.75" rx="1.5" ry="1.75" fill="black"/>
<ellipse cx="13" cy="5.75" rx="1.5" ry="1.75" fill="black"/>
<path d="M4.66558 10.6543C4.47466 10.2867 4.0219 10.1435 3.65431 10.3344C3.28672 10.5253 3.1435 10.9781 3.33442 11.3457L4.66558 10.6543ZM14.678 11.3206C14.8551 10.9462 14.6951 10.4991 14.3206 10.322C13.9462 10.1449 13.4991 10.3049 13.322 10.6794L14.678 11.3206ZM4 11C3.33442 11.3457 3.33458 11.346 3.33475 11.3463C3.33481 11.3464 3.33499 11.3468 3.33512 11.347C3.33537 11.3475 3.33565 11.3481 3.33596 11.3486C3.33657 11.3498 3.33729 11.3512 3.3381 11.3527C3.33972 11.3558 3.34175 11.3596 3.34417 11.3641C3.34901 11.3731 3.35546 11.3849 3.36353 11.3993C3.37966 11.4282 3.40229 11.4677 3.43154 11.5162C3.48998 11.6131 3.57517 11.7467 3.68808 11.9048C3.91323 12.2199 4.25254 12.6377 4.71468 13.056C5.64215 13.8956 7.08135 14.75 9.06977 14.75V13.25C7.54656 13.25 6.45087 12.6044 5.72136 11.944C5.35502 11.6123 5.08532 11.2801 4.90858 11.0327C4.82054 10.9095 4.75657 10.8088 4.71608 10.7416C4.69586 10.7081 4.68159 10.6831 4.67318 10.668C4.66898 10.6605 4.66625 10.6555 4.66499 10.6531C4.66435 10.652 4.66409 10.6515 4.66419 10.6516C4.66424 10.6517 4.66438 10.652 4.66461 10.6524C4.66473 10.6527 4.66487 10.6529 4.66503 10.6532C4.66511 10.6534 4.66525 10.6537 4.66529 10.6537C4.66543 10.654 4.66558 10.6543 4 11ZM9.06977 14.75C11.0611 14.75 12.4696 13.893 13.3669 13.0451C13.8129 12.6236 14.1351 12.2027 14.3471 11.8853C14.4535 11.7261 14.5331 11.5914 14.5875 11.4936C14.6148 11.4446 14.6358 11.4047 14.6508 11.3754C14.6583 11.3608 14.6643 11.3488 14.6688 11.3396C14.6711 11.335 14.673 11.3311 14.6745 11.3279C14.6753 11.3263 14.676 11.3249 14.6765 11.3237C14.6768 11.3231 14.6771 11.3225 14.6774 11.322C14.6775 11.3218 14.6776 11.3214 14.6777 11.3213C14.6779 11.3209 14.678 11.3206 14 11C13.322 10.6794 13.3221 10.6791 13.3223 10.6788C13.3223 10.6787 13.3224 10.6784 13.3225 10.6783C13.3227 10.6779 13.3228 10.6776 13.3229 10.6774C13.3232 10.6769 13.3233 10.6766 13.3234 10.6764C13.3235 10.6762 13.3233 10.6766 13.3228 10.6776C13.3217 10.6798 13.3193 10.6846 13.3156 10.6919C13.3081 10.7066 13.2952 10.7312 13.2768 10.7642C13.24 10.8304 13.1813 10.9301 13.0998 11.0522C12.9361 11.2973 12.6842 11.6264 12.3366 11.9549C11.6466 12.607 10.5901 13.25 9.06977 13.25V14.75Z" fill="black"/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M20 1C19.4477 1 19 1.44772 19 2V4H17C16.4477 4 16 4.44771 16 5C16 5.55228 16.4477 6 17 6H19V8C19 8.55228 19.4477 9 20 9C20.5523 9 21 8.55228 21 8V6H23C23.5523 6 24 5.55228 24 5C24 4.44772 23.5523 4 23 4H21V2C21 1.44772 20.5523 1 20 1ZM7 9.5C7 8.67 7.67 8 8.5 8C9.33 8 10 8.67 10 9.5C10 10.33 9.33 11 8.5 11C7.67 11 7 10.33 7 9.5ZM15.5 11C16.33 11 17 10.33 17 9.5C17 8.67 16.33 8 15.5 8C14.67 8 14 8.67 14 9.5C14 10.33 14.67 11 15.5 11ZM12 17.5C14.33 17.5 16.31 16.04 17.11 14H6.89001C7.69001 16.04 9.67001 17.5 12 17.5ZM4 12C4 7.58172 7.58172 4 12 4C12.6108 4 13.2045 4.06827 13.7742 4.1972C14.3129 4.3191 14.8484 3.98125 14.9703 3.44258C15.0922 2.90392 14.7543 2.36843 14.2156 2.24653C13.502 2.08504 12.7603 2 12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 11.7878 21.9934 11.5771 21.9803 11.368C21.9459 10.8168 21.4711 10.3978 20.9199 10.4323C20.3687 10.4667 19.9498 10.9414 19.9842 11.4926C19.9947 11.6603 20 11.8295 20 12C20 16.4183 16.4183 20 12 20C7.58172 20 4 16.4183 4 12Z" fill="#737D8C"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -1,7 +1,7 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.5151 20.0831L15.6941 17.2621L17.2621 15.6941L20.0831 18.5151C21.5741 20.0061 22.1529 21.7793 21.9661 21.9661C21.7793 22.1529 20.0061 21.5741 18.5151 20.0831Z" fill="black"/>
<path d="M7.46196 11.3821C7.07677 11.5059 5.49073 12.0989 3.63366 12.0744C1.77658 12.0499 1.67795 10.8941 2.46811 10.1039L6.28598 6.28602L9.42196 9.42203L7.46196 11.3821Z" fill="black"/>
<path d="M11.3821 7.46202C11.5059 7.07682 12.0989 5.49077 12.0744 3.63368C12.0499 1.77658 10.8941 1.67795 10.1039 2.46812L6.28598 6.28602L9.42196 9.42203L11.3821 7.46202Z" fill="black"/>
<path d="M7.40596 11.438L11.4379 7.40602L14.9099 10.206L10.2059 14.9101L7.40596 11.438Z" fill="black"/>
<path d="M11.774 11.774C9.31114 14.2369 8.61779 17.7115 9.83827 20.3213C10.3104 21.3308 11.6288 21.3273 12.4169 20.5392L20.5391 12.4169C21.3271 11.6289 21.3307 10.3104 20.3212 9.83829C17.7114 8.61779 14.2369 9.31115 11.774 11.774Z" fill="black"/>
<path d="M18.5151 20.0831L15.6941 17.2621L17.2621 15.6941L20.0831 18.5151C21.5741 20.0061 22.1529 21.7793 21.9661 21.9661C21.7793 22.1529 20.0061 21.5741 18.5151 20.0831Z" fill="#737D8C"/>
<path d="M7.46196 11.3821C7.07677 11.5059 5.49073 12.0989 3.63366 12.0744C1.77658 12.0499 1.67795 10.8941 2.46811 10.1039L6.28598 6.28602L9.42196 9.42203L7.46196 11.3821Z" fill="#737D8C"/>
<path d="M11.3821 7.46202C11.5059 7.07682 12.0989 5.49077 12.0744 3.63368C12.0499 1.77658 10.8941 1.67795 10.1039 2.46812L6.28598 6.28602L9.42196 9.42203L11.3821 7.46202Z" fill="#737D8C"/>
<path d="M7.40596 11.438L11.4379 7.40602L14.9099 10.206L10.2059 14.9101L7.40596 11.438Z" fill="#737D8C"/>
<path d="M11.774 11.774C9.31114 14.2369 8.61779 17.7115 9.83827 20.3213C10.3104 21.3308 11.6288 21.3273 12.4169 20.5392L20.5391 12.4169C21.3271 11.6289 21.3307 10.3104 20.3212 9.83829C17.7114 8.61779 14.2369 9.31115 11.774 11.774Z" fill="#737D8C"/>
</svg>

Before

Width:  |  Height:  |  Size: 1,015 B

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1,141 +1,96 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink" preserveAspectRatio="none" viewBox="0 0 375 375" style="background-color:#FFFFFF00; overflow:visible">
<title>start</title>
<!-- Layers -->
<!-- Layer: Icon -->
<svg x="188" y="187" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="188;187.75;187.5"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="187;187.25;187.5"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<animateTransform attributeName="transform" calcMode="spline" dur="2" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1"
repeatCount="indefinite" type="rotate" values="0;180;360"/>
<svg x="-100" y="-100" width="200" height="200" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-100;-117.5;-100"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-100;-117.5;-100"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="200;235;200"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 1 1;0 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="200;235;200"/>
<g clip-path="">
<g filter="">
<!-- Layer: 1024@2x -->
<svg x="100" y="100" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="100;117.5;100"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="100;117.5;100"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<svg x="-100" y="-100" width="200" height="200" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-100;-117.5;-100"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-100;-117.5;-100"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="200;235;200"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="200;235;200"/>
<g clip-path="">
<g filter="">
<!-- Layer: Path -->
<svg x="118" y="46" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="118;138.65;118"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="46;54.05;46"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<svg x="-46" y="-46" width="92" height="92" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<g clip-path="">
<g filter="">
<path d="M0,12c0,-6.627,5.373,-12,12,-12 44.183,0,80,35.817,80,80 0,6.627,-5.373,12,-12,12 -6.627,0,-12,-5.373,-12,-12 0,-30.928,-25.072,-56,-56,-56 -6.627,0,-12,-5.373,-12,-12zM0,12" fill="#0DBD8B" id="path" stroke="#00000000" stroke-dasharray="0" stroke-dashoffset="0" stroke-miterlimit="10" stroke-width="0">
<animate attributeName="d" calcMode="spline" dur="2s" fill="freeze" href="#path" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="M0,12c0,-6.627,5.373,-12,12,-12 44.183,0,80,35.817,80,80 0,6.627,-5.373,12,-12,12 -6.627,0,-12,-5.373,-12,-12 0,-30.928,-25.072,-56,-56,-56 -6.627,0,-12,-5.373,-12,-12zM0,12;M0,14.1c0,-7.787,6.313,-14.1,14.1,-14.1 51.915,0,94,42.085,94,94 0,7.787,-6.313,14.1,-14.1,14.1 -7.787,0,-14.1,-6.313,-14.1,-14.1 0,-36.34,-29.46,-65.8,-65.8,-65.8 -7.787,0,-14.1,-6.313,-14.1,-14.1zM0,14.1;M0,12c0,-6.627,5.373,-12,12,-12 44.183,0,80,35.817,80,80 0,6.627,-5.373,12,-12,12 -6.627,0,-12,-5.373,-12,-12 0,-30.928,-25.072,-56,-56,-56 -6.627,0,-12,-5.373,-12,-12zM0,12"/>
</path>
</g>
</g>
</svg>
</g>
</g>
</svg>
<!-- Layer: Path -->
<svg x="82" y="154" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="82;96.35;82"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="154;180.95;154"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<svg x="-46" y="-46" width="92" height="92" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<g clip-path="">
<g filter="">
<path d="M92,80c0,6.627,-5.373,12,-12,12 -44.183,0,-80,-35.817,-80,-80 0,-6.627,5.373,-12,12,-12 6.627,0,12,5.373,12,12 0,30.928,25.072,56,56,56 6.627,0,12,5.373,12,12zM92,80" fill="#0DBD8B" id="path_1" stroke="#00000000" stroke-dasharray="0" stroke-dashoffset="0" stroke-miterlimit="10" stroke-width="0">
<animate attributeName="d" calcMode="spline" dur="2s" fill="freeze" href="#path_1" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="M92,80c0,6.627,-5.373,12,-12,12 -44.183,0,-80,-35.817,-80,-80 0,-6.627,5.373,-12,12,-12 6.627,0,12,5.373,12,12 0,30.928,25.072,56,56,56 6.627,0,12,5.373,12,12zM92,80;M108.1,94c0,7.787,-6.313,14.1,-14.1,14.1 -51.915,0,-94,-42.085,-94,-94 0,-7.787,6.313,-14.1,14.1,-14.1 7.787,0,14.1,6.313,14.1,14.1 0,36.34,29.46,65.8,65.8,65.8 7.787,0,14.1,6.313,14.1,14.1zM108.1,94;M92,80c0,6.627,-5.373,12,-12,12 -44.183,0,-80,-35.817,-80,-80 0,-6.627,5.373,-12,12,-12 6.627,0,12,5.373,12,12 0,30.928,25.072,56,56,56 6.627,0,12,5.373,12,12zM92,80"/>
</path>
</g>
</g>
</svg>
</g>
</g>
</svg>
<!-- Layer: Path -->
<svg x="46" y="82" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="46;54.05;46"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="82;96.35;82"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<svg x="-46" y="-46" width="92" height="92" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<g clip-path="">
<g filter="">
<path d="M12,92c-6.627,0,-12,-5.373,-12,-12 0,-44.183,35.817,-80,80,-80 6.627,0,12,5.373,12,12 0,6.627,-5.373,12,-12,12 -30.928,0,-56,25.072,-56,56 0,6.627,-5.373,12,-12,12zM12,92" fill="#0DBD8B" id="path_2" stroke="#00000000" stroke-dasharray="0" stroke-dashoffset="0" stroke-miterlimit="10" stroke-width="0">
<animate attributeName="d" calcMode="spline" dur="2s" fill="freeze" href="#path_2" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="M12,92c-6.627,0,-12,-5.373,-12,-12 0,-44.183,35.817,-80,80,-80 6.627,0,12,5.373,12,12 0,6.627,-5.373,12,-12,12 -30.928,0,-56,25.072,-56,56 0,6.627,-5.373,12,-12,12zM12,92;M14.1,108.1c-7.787,0,-14.1,-6.313,-14.1,-14.1 0,-51.915,42.085,-94,94,-94 7.787,0,14.1,6.313,14.1,14.1 0,7.787,-6.313,14.1,-14.1,14.1 -36.34,0,-65.8,29.46,-65.8,65.8 0,7.787,-6.313,14.1,-14.1,14.1zM14.1,108.1;M12,92c-6.627,0,-12,-5.373,-12,-12 0,-44.183,35.817,-80,80,-80 6.627,0,12,5.373,12,12 0,6.627,-5.373,12,-12,12 -30.928,0,-56,25.072,-56,56 0,6.627,-5.373,12,-12,12zM12,92"/>
</path>
</g>
</g>
</svg>
</g>
</g>
</svg>
<!-- Layer: Path -->
<svg x="154" y="118" width="0.01" height="0.01" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="154;180.95;154"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="118;138.65;118"/>
<g transform="scale(1 1)">
<g transform="rotate(0)">
<svg x="-46" y="-46" width="92" height="92" style ="overflow:visible" opacity="1">
<animate attributeName="x" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="y" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="-46;-54.05;-46"/>
<animate attributeName="width" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<animate attributeName="height" calcMode="spline" dur="2s" fill="freeze" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="92;108.1;92"/>
<g clip-path="">
<g filter="">
<path d="M80,0c6.627,0,12,5.373,12,12 0,44.183,-35.817,80,-80,80 -6.627,0,-12,-5.373,-12,-12 0,-6.627,5.373,-12,12,-12 30.928,0,56,-25.072,56,-56 0,-6.627,5.373,-12,12,-12zM80,0" fill="#0DBD8B" id="path_3" stroke="#00000000" stroke-dasharray="0" stroke-dashoffset="0" stroke-miterlimit="10" stroke-width="0">
<animate attributeName="d" calcMode="spline" dur="2s" fill="freeze" href="#path_3" keySplines="0.42 0 0.58 1;0.42 0 0.58 1" keyTimes="0;0.5;1" repeatCount="indefinite" values="M80,0c6.627,0,12,5.373,12,12 0,44.183,-35.817,80,-80,80 -6.627,0,-12,-5.373,-12,-12 0,-6.627,5.373,-12,12,-12 30.928,0,56,-25.072,56,-56 0,-6.627,5.373,-12,12,-12zM80,0;M94,0c7.787,0,14.1,6.313,14.1,14.1 0,51.915,-42.085,94,-94,94 -7.787,0,-14.1,-6.313,-14.1,-14.1 0,-7.787,6.313,-14.1,14.1,-14.1 36.34,0,65.8,-29.46,65.8,-65.8 0,-7.787,6.313,-14.1,14.1,-14.1zM94,0;M80,0c6.627,0,12,5.373,12,12 0,44.183,-35.817,80,-80,80 -6.627,0,-12,-5.373,-12,-12 0,-6.627,5.373,-12,12,-12 30.928,0,56,-25.072,56,-56 0,-6.627,5.373,-12,12,-12zM80,0"/>
</path>
</g>
</g>
</svg>
</g>
</g>
</svg>
</g>
</g>
</svg>
</g>
</g>
</svg>
</g>
</g>
</svg>
</g>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="128"
height="128"
viewBox="0 0 33.866666 33.866668"
version="1.1"
id="svg920"
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
sodipodi:docname="spinner.svg">
<defs
id="defs914" />
<metadata
id="metadata917">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="stroke-width:0;fill-opacity:0.30000001"
d="M 59,95.605469 V 123 c 0,2.77 2.23,5 5,5 2.77,0 5,-2.23 5,-5 V 95.605469 A 31.999998,31.999998 0 0 1 64,96 31.999998,31.999998 0 0 1 59,95.605469 Z"
transform="scale(0.26458333)"
id="path2350" />
<path
style="stroke-width:0;fill-opacity:0.7020452"
d="M 64,0 C 61.23,0 59,2.2300001 59,5 V 32.394531 A 31.999998,31.999998 0 0 1 64,32 31.999998,31.999998 0 0 1 69,32.394531 V 5 C 69,2.2300001 66.77,0 64,0 Z"
transform="scale(0.26458333)"
id="rect2283" />
<path
style="stroke-width:0;fill-opacity:0.30000001"
d="M 43.867188,88.871094 30.169922,112.5957 c -1.385,2.39889 -0.568812,5.44508 1.830078,6.83008 2.39889,1.385 5.445078,0.56881 6.830078,-1.83008 L 52.527344,93.873047 a 31.999998,31.999998 0 0 1 -8.660156,-5.001953 z"
transform="scale(0.26458333)"
id="path2346" />
<path
style="stroke-width:0;fill-opacity:0.80019373"
d="m 93.150391,7.9121094 c -1.599848,0.111837 -3.114844,0.992881 -3.980469,2.4921876 L 75.472656,34.126953 a 31.999998,31.999998 0 0 1 8.660156,5.001953 L 97.830078,15.404297 C 99.215078,13.005407 98.39889,9.9592187 96,8.5742188 95.100416,8.0548438 94.110299,7.8450072 93.150391,7.9121094 Z"
transform="scale(0.26458333)"
id="rect2285" />
<path
style="stroke-width:0;fill-opacity:0.30000001"
d="M 34.126953,75.474609 10.404297,89.169922 C 8.0054066,90.554922 7.1892188,93.60111 8.5742188,96 c 1.3849999,2.39889 4.4311882,3.215078 6.8300782,1.830078 L 39.128906,84.132812 a 31.999998,31.999998 0 0 1 -5.001953,-8.658203 z"
transform="scale(0.26458333)"
id="path2342" />
<path
style="stroke-width:0;fill-opacity:0.90226436"
d="m 115.44531,29.507812 c -0.95991,-0.0671 -1.95002,0.142735 -2.84961,0.66211 L 88.871094,43.867188 a 31.999998,31.999998 0 0 1 5.001953,8.658203 L 117.5957,38.830078 c 2.39889,-1.385 3.21508,-4.431188 1.83008,-6.830078 -0.86562,-1.499306 -2.38062,-2.38035 -3.98047,-2.492188 z"
transform="scale(0.26458333)"
id="rect2287" />
<path
style="stroke-width:0;fill-opacity:1"
d="M 95.605469,59 A 31.999998,31.999998 0 0 1 96,64 31.999998,31.999998 0 0 1 95.605469,69 H 123 c 2.77,0 5,-2.23 5,-5 0,-2.77 -2.23,-5 -5,-5 z"
transform="scale(0.26458333)"
id="path2338" />
<path
style="stroke-width:0;fill-opacity:0.40288368"
d="m 5,59 c -2.7699999,0 -5,2.23 -5,5 0,2.77 2.2300001,5 5,5 H 32.394531 A 31.999998,31.999998 0 0 1 32,64 31.999998,31.999998 0 0 1 32.394531,59 Z"
transform="scale(0.26458333)"
id="rect2289" />
<path
style="stroke-width:0;fill-opacity:0.30000001"
d="m 93.873047,75.472656 a 31.999998,31.999998 0 0 1 -5.001953,8.660156 L 112.5957,97.830078 c 2.39889,1.385 5.44508,0.568812 6.83008,-1.830078 1.385,-2.39889 0.56881,-5.445078 -1.83008,-6.830078 z"
transform="scale(0.26458333)"
id="path2334" />
<path
style="stroke-width:0;fill-opacity:0.49898377"
d="M 12.554688,29.507812 C 10.95484,29.61965 9.4398437,30.500694 8.5742188,32 c -1.385,2.39889 -0.5688122,5.445078 1.8300782,6.830078 l 23.722656,13.697266 a 31.999998,31.999998 0 0 1 5.001953,-8.660156 L 15.404297,30.169922 c -0.899584,-0.519375 -1.889701,-0.729212 -2.849609,-0.66211 z"
transform="scale(0.26458333)"
id="rect2291" />
<path
style="stroke-width:0;fill-opacity:0.30000001"
d="m 84.132812,88.871094 a 31.999998,31.999998 0 0 1 -8.658203,5.001953 L 89.169922,117.5957 c 1.385,2.39889 4.431188,3.21508 6.830078,1.83008 2.39889,-1.385 3.215078,-4.43119 1.830078,-6.83008 z"
transform="scale(0.26458333)"
id="path2330" />
<path
style="stroke-width:0;fill-opacity:0.5998317"
d="M 34.849609,7.9121094 C 33.889701,7.8450072 32.899584,8.0548438 32,8.5742188 29.60111,9.9592187 28.784922,13.005407 30.169922,15.404297 l 13.697266,23.724609 a 31.999998,31.999998 0 0 1 8.658203,-5.001953 L 38.830078,10.404297 C 37.964453,8.9049904 36.449457,8.0239464 34.849609,7.9121094 Z"
transform="scale(0.26458333)"
id="rect2293" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View file

@ -9,6 +9,7 @@ $header-panel-text-primary-color: #B9BEC6;
$header-panel-text-secondary-color: #c8c8cd;
$text-primary-color: #ffffff;
$text-secondary-color: #B9BEC6;
$quaternary-fg-color: #6F7882;
$search-bg-color: #181b21;
$search-placeholder-color: #61708b;
$room-highlight-color: #343a46;
@ -205,9 +206,18 @@ $breadcrumb-placeholder-bg-color: #272c35;
$user-tile-hover-bg-color: $header-panel-bg-color;
$message-body-panel-bg-color: #21262c82;
$message-body-panel-icon-bg-color: #8e99a4;
$message-body-panel-fg-color: $primary-fg-color;
$message-body-panel-fg-color: $secondary-fg-color;
$message-body-panel-bg-color: #394049; // "Dark Tile"
$message-body-panel-icon-fg-color: #21262C; // "Separator"
$message-body-panel-icon-bg-color: $tertiary-fg-color;
$voice-record-stop-border-color: $quaternary-fg-color;
$voice-record-waveform-bg-color: $message-body-panel-bg-color;
$voice-record-waveform-fg-color: $message-body-panel-fg-color;
$voice-record-waveform-incomplete-fg-color: $quaternary-fg-color;
$voice-record-icon-color: $quaternary-fg-color;
$voice-playback-button-bg-color: $message-body-panel-icon-bg-color;
$voice-playback-button-fg-color: $message-body-panel-icon-fg-color;
// Appearance tab colors
$appearance-tab-border-color: $room-highlight-color;

View file

@ -200,9 +200,19 @@ $breadcrumb-placeholder-bg-color: #272c35;
$user-tile-hover-bg-color: $header-panel-bg-color;
$message-body-panel-bg-color: #21262c82;
$message-body-panel-icon-bg-color: #8e99a4;
$message-body-panel-fg-color: $primary-fg-color;
$message-body-panel-fg-color: $secondary-fg-color;
$message-body-panel-bg-color: #394049;
$message-body-panel-icon-fg-color: $primary-bg-color;
$message-body-panel-icon-bg-color: $secondary-fg-color;
// See non-legacy dark for variable information
$voice-record-stop-border-color: #6F7882;
$voice-record-waveform-bg-color: $message-body-panel-bg-color;
$voice-record-waveform-fg-color: $message-body-panel-fg-color;
$voice-record-waveform-incomplete-fg-color: #6F7882;
$voice-record-icon-color: #6F7882;
$voice-playback-button-bg-color: $tertiary-fg-color;
$voice-playback-button-fg-color: #21262C;
// Appearance tab colors
$appearance-tab-border-color: $room-highlight-color;

View file

@ -191,14 +191,6 @@ $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%)
$groupFilterPanel-divider-color: $roomlist-header-color;
// See non-legacy _light for variable information
$voice-record-stop-border-color: #E3E8F0;
$voice-record-stop-symbol-color: #ff4b55;
$voice-record-waveform-bg-color: #E3E8F0;
$voice-record-waveform-fg-color: $muted-fg-color;
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
$voice-record-live-circle-color: #ff4b55;
$roomtile-preview-color: #9e9e9e;
$roomtile-default-badge-bg-color: #61708b;
$roomtile-selected-bg-color: #fff;
@ -331,9 +323,21 @@ $breadcrumb-placeholder-bg-color: #e8eef5;
$user-tile-hover-bg-color: $header-panel-bg-color;
$message-body-panel-bg-color: #e3e8f082;
$message-body-panel-icon-bg-color: #ffffff;
$message-body-panel-fg-color: $muted-fg-color;
$message-body-panel-fg-color: $secondary-fg-color;
$message-body-panel-bg-color: #E3E8F0;
$message-body-panel-icon-fg-color: $secondary-fg-color;
$message-body-panel-icon-bg-color: $primary-bg-color;
// See non-legacy _light for variable information
$voice-record-stop-symbol-color: #ff4b55;
$voice-record-live-circle-color: #ff4b55;
$voice-record-stop-border-color: #E3E8F0;
$voice-record-waveform-bg-color: $message-body-panel-bg-color;
$voice-record-waveform-fg-color: $message-body-panel-fg-color;
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
$voice-record-icon-color: $tertiary-fg-color;
$voice-playback-button-bg-color: $message-body-panel-icon-bg-color;
$voice-playback-button-fg-color: $message-body-panel-icon-fg-color;
// FontSlider colors
$appearance-tab-border-color: $input-darker-bg-color;

View file

@ -21,6 +21,7 @@ $notice-primary-bg-color: rgba(255, 75, 85, 0.16);
$primary-fg-color: #2e2f32;
$secondary-fg-color: #737D8C;
$tertiary-fg-color: #8D99A5;
$quaternary-fg-color: #C1C6CD;
$header-panel-bg-color: #f3f8fd;
// typical text (dark-on-white in light skin)
@ -182,13 +183,6 @@ $roomsublist-skeleton-ui-bg: linear-gradient(180deg, #ffffff 0%, #ffffff00 100%)
$groupFilterPanel-divider-color: $roomlist-header-color;
$voice-record-stop-border-color: #E3E8F0;
$voice-record-stop-symbol-color: #ff4b55; // $warning-color, but without letting people change it in themes
$voice-record-waveform-bg-color: #E3E8F0;
$voice-record-waveform-fg-color: $muted-fg-color;
$voice-record-waveform-incomplete-fg-color: #C1C6CD;
$voice-record-live-circle-color: #ff4b55; // $warning-color, but without letting people change it in themes
$roomtile-preview-color: $secondary-fg-color;
$roomtile-default-badge-bg-color: #61708b;
$roomtile-selected-bg-color: #FFF;
@ -328,9 +322,23 @@ $breadcrumb-placeholder-bg-color: #e8eef5;
$user-tile-hover-bg-color: $header-panel-bg-color;
$message-body-panel-bg-color: #e3e8f082;
$message-body-panel-icon-bg-color: #ffffff;
$message-body-panel-fg-color: $muted-fg-color;
$message-body-panel-fg-color: $secondary-fg-color;
$message-body-panel-bg-color: #E3E8F0; // "Separator"
$message-body-panel-icon-fg-color: $secondary-fg-color;
$message-body-panel-icon-bg-color: $primary-bg-color;
// These two don't change between themes. They are the $warning-color, but we don't
// want custom themes to affect them by accident.
$voice-record-stop-symbol-color: #ff4b55;
$voice-record-live-circle-color: #ff4b55;
$voice-record-stop-border-color: #E3E8F0; // "Separator"
$voice-record-waveform-bg-color: $message-body-panel-bg-color;
$voice-record-waveform-fg-color: $message-body-panel-fg-color;
$voice-record-waveform-incomplete-fg-color: $quaternary-fg-color;
$voice-record-icon-color: $tertiary-fg-color;
$voice-playback-button-bg-color: $message-body-panel-icon-bg-color;
$voice-playback-button-fg-color: $message-body-panel-icon-fg-color;
// FontSlider colors
$appearance-tab-border-color: $input-darker-bg-color;

View file

@ -42,6 +42,8 @@ import {SpaceStoreClass} from "../stores/SpaceStore";
import TypingStore from "../stores/TypingStore";
import { EventIndexPeg } from "../indexing/EventIndexPeg";
import {VoiceRecordingStore} from "../stores/VoiceRecordingStore";
import PerformanceMonitor from "../performance";
import UIStore from "../stores/UIStore";
declare global {
interface Window {
@ -52,6 +54,9 @@ declare global {
init: () => Promise<void>;
};
// Needed for Safari, unknown to TypeScript
webkitAudioContext: typeof AudioContext;
mxContentMessages: ContentMessages;
mxToastStore: ToastStore;
mxDeviceListener: DeviceListener;
@ -76,6 +81,9 @@ declare global {
mxVoiceRecordingStore: VoiceRecordingStore;
mxTypingStore: TypingStore;
mxEventIndexPeg: EventIndexPeg;
mxPerformanceMonitor: PerformanceMonitor;
mxPerformanceEntryNames: any;
mxUIStore: UIStore;
}
interface Document {
@ -118,6 +126,16 @@ declare global {
interface HTMLAudioElement {
type?: string;
// sinkId & setSinkId are experimental and typescript doesn't know about them
sinkId: string;
setSinkId(outputId: string);
}
interface HTMLVideoElement {
type?: string;
// sinkId & setSinkId are experimental and typescript doesn't know about them
sinkId: string;
setSinkId(outputId: string);
}
interface Element {

View file

@ -1,6 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2020 The Matrix.org Foundation C.I.C.
Copyright 2015-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.
@ -15,52 +14,61 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import React, { ComponentType } from "react";
import * as sdk from './index';
import PropTypes from 'prop-types';
import { _t } from './languageHandler';
import { IDialogProps } from "./components/views/dialogs/IDialogProps";
type AsyncImport<T> = { default: T };
interface IProps extends IDialogProps {
// A promise which resolves with the real component
prom: Promise<ComponentType | AsyncImport<ComponentType>>;
}
interface IState {
component?: ComponentType;
error?: Error;
}
/**
* Wrap an asynchronous loader function with a react component which shows a
* spinner until the real component loads.
*/
export default class AsyncWrapper extends React.Component {
static propTypes = {
/** A promise which resolves with the real component
*/
prom: PropTypes.object.isRequired,
};
export default class AsyncWrapper extends React.Component<IProps, IState> {
private unmounted = false;
state = {
public state = {
component: null,
error: null,
};
componentDidMount() {
this._unmounted = false;
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/element-web/issues/3148
console.log('Starting load of AsyncWrapper for modal');
this.props.prom.then((result) => {
if (this._unmounted) {
return;
}
if (this.unmounted) return;
// Take the 'default' member if it's there, then we support
// passing in just an import()ed module, since ES6 async import
// always returns a module *namespace*.
const component = result.default ? result.default : result;
this.setState({component});
const component = (result as AsyncImport<ComponentType>).default
? (result as AsyncImport<ComponentType>).default
: result as ComponentType;
this.setState({ component });
}).catch((e) => {
console.warn('AsyncWrapper promise failed', e);
this.setState({error: e});
this.setState({ error: e });
});
}
componentWillUnmount() {
this._unmounted = true;
this.unmounted = true;
}
_onWrapperCancelClick = () => {
private onWrapperCancelClick = () => {
this.props.onFinished(false);
};
@ -71,12 +79,10 @@ export default class AsyncWrapper extends React.Component {
} else if (this.state.error) {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <BaseDialog onFinished={this.props.onFinished}
title={_t("Error")}
>
{_t("Unable to load! Check your network connectivity and try again.")}
return <BaseDialog onFinished={this.props.onFinished} title={_t("Error")}>
{ _t("Unable to load! Check your network connectivity and try again.") }
<DialogButtons primaryButton={_t("Dismiss")}
onPrimaryButtonClick={this._onWrapperCancelClick}
onPrimaryButtonClick={this.onWrapperCancelClick}
hasCancel={false}
/>
</BaseDialog>;

View file

@ -20,6 +20,7 @@ import {Room} from "matrix-js-sdk/src/models/room";
import DMRoomMap from './utils/DMRoomMap';
import {mediaFromMxc} from "./customisations/Media";
import SettingsStore from "./settings/SettingsStore";
export type ResizeMethod = "crop" | "scale";
@ -143,7 +144,7 @@ export function avatarUrlForRoom(room: Room, width: number, height: number, resi
}
// space rooms cannot be DMs so skip the rest
if (room.isSpaceRoom()) return null;
if (SettingsStore.getValue("feature_spaces") && room.isSpaceRoom()) return null;
let otherMember = null;
const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId);

View file

@ -85,6 +85,7 @@ import { Action } from './dispatcher/actions';
import VoipUserMapper from './VoipUserMapper';
import { addManagedHybridWidget, isManagedHybridWidgetEnabled } from './widgets/ManagedHybrid';
import { randomUppercaseString, randomLowercaseString } from "matrix-js-sdk/src/randomstring";
import EventEmitter from 'events';
import SdkConfig from './SdkConfig';
import { ensureDMExists, findDMForUser } from './createRoom';
@ -138,22 +139,12 @@ export enum PlaceCallType {
ScreenSharing = 'screensharing',
}
function getRemoteAudioElement(): HTMLAudioElement {
// this needs to be somewhere at the top of the DOM which
// always exists to avoid audio interruptions.
// Might as well just use DOM.
const remoteAudioElement = document.getElementById("remoteAudio") as HTMLAudioElement;
if (!remoteAudioElement) {
console.error(
"Failed to find remoteAudio element - cannot play audio!" +
"You need to add an <audio/> to the DOM.",
);
return null;
}
return remoteAudioElement;
export enum CallHandlerEvent {
CallsChanged = "calls_changed",
CallChangeRoom = "call_change_room",
}
export default class CallHandler {
export default class CallHandler extends EventEmitter {
private calls = new Map<string, MatrixCall>(); // roomId -> call
// Calls started as an attended transfer, ie. with the intention of transferring another
// call with a different party to this one.
@ -273,7 +264,7 @@ export default class CallHandler {
}
public getSupportsVirtualRooms() {
return this.supportsPstnProtocol;
return this.supportsSipNativeVirtual;
}
public pstnLookup(phoneNumber: string): Promise<ThirdpartyLookupResponse[]> {
@ -471,6 +462,9 @@ export default class CallHandler {
if (call.hangupReason === CallErrorCode.UserHangup) {
title = _t("Call Declined");
description = _t("The other party declined the call.");
} else if (call.hangupReason === CallErrorCode.UserBusy) {
title = _t("User Busy");
description = _t("The user you called is busy.");
} else if (call.hangupReason === CallErrorCode.InviteTimeout) {
title = _t("Call Failed");
// XXX: full stop appended as some relic here, but these
@ -514,6 +508,7 @@ export default class CallHandler {
}
this.calls.set(mappedRoomId, newCall);
this.emit(CallHandlerEvent.CallsChanged, this.calls);
this.setCallListeners(newCall);
this.setCallState(newCall, newCall.state);
});
@ -526,7 +521,9 @@ export default class CallHandler {
let newNativeAssertedIdentity = newAssertedIdentity;
if (newAssertedIdentity) {
const response = await this.sipNativeLookup(newAssertedIdentity);
if (response.length) newNativeAssertedIdentity = response[0].userid;
if (response.length && response[0].fields.lookup_success) {
newNativeAssertedIdentity = response[0].userid;
}
}
console.log(`Asserted identity ${newAssertedIdentity} mapped to ${newNativeAssertedIdentity}`);
@ -545,11 +542,9 @@ export default class CallHandler {
if (newMappedRoomId !== mappedRoomId) {
this.removeCallForRoom(mappedRoomId);
mappedRoomId = newMappedRoomId;
console.log("Moving call to room " + mappedRoomId);
this.calls.set(mappedRoomId, call);
dis.dispatch({
action: Action.CallChangeRoom,
call,
});
this.emit(CallHandlerEvent.CallChangeRoom, call);
}
}
});
@ -598,11 +593,6 @@ export default class CallHandler {
}
}
private setCallAudioElement(call: MatrixCall) {
const audioElement = getRemoteAudioElement();
if (audioElement) call.setRemoteAudioElement(audioElement);
}
private setCallState(call: MatrixCall, status: CallState) {
const mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call);
@ -618,7 +608,9 @@ export default class CallHandler {
}
private removeCallForRoom(roomId: string) {
console.log("Removing call for room ", roomId);
this.calls.delete(roomId);
this.emit(CallHandlerEvent.CallsChanged, this.calls);
}
private showICEFallbackPrompt() {
@ -679,11 +671,7 @@ export default class CallHandler {
}, null, true);
}
private async placeCall(
roomId: string, type: PlaceCallType,
localElement: HTMLVideoElement, remoteElement: HTMLVideoElement,
transferee: MatrixCall,
) {
private async placeCall(roomId: string, type: PlaceCallType, transferee: MatrixCall) {
Analytics.trackEvent('voip', 'placeCall', 'type', type);
CountlyAnalytics.instance.trackStartCall(roomId, type === PlaceCallType.Video, false);
@ -694,23 +682,21 @@ export default class CallHandler {
console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms");
const call = MatrixClientPeg.get().createCall(mappedRoomId);
console.log("Adding call for room ", roomId);
this.calls.set(roomId, call);
this.emit(CallHandlerEvent.CallsChanged, this.calls);
if (transferee) {
this.transferees[call.callId] = transferee;
}
this.setCallListeners(call);
this.setCallAudioElement(call);
this.setActiveCallRoomId(roomId);
if (type === PlaceCallType.Voice) {
call.placeVoiceCall();
} else if (type === 'video') {
call.placeVideoCall(
remoteElement,
localElement,
);
call.placeVideoCall();
} else if (type === PlaceCallType.ScreenSharing) {
const screenCapErrorString = PlatformPeg.get().screenCaptureErrorString();
if (screenCapErrorString) {
@ -724,13 +710,12 @@ export default class CallHandler {
}
call.placeScreenSharingCall(
remoteElement,
localElement,
async (): Promise<DesktopCapturerSource> => {
const {finished} = Modal.createDialog(DesktopCapturerSourcePicker);
const [source] = await finished;
return source;
});
},
);
} else {
console.error("Unknown conf call type: " + type);
}
@ -787,17 +772,12 @@ export default class CallHandler {
} else if (members.length === 2) {
console.info(`Place ${payload.type} call in ${payload.room_id}`);
this.placeCall(
payload.room_id, payload.type, payload.local_element, payload.remote_element,
payload.transferee,
);
this.placeCall(payload.room_id, payload.type, payload.transferee);
} else { // > 2
dis.dispatch({
action: "place_conference_call",
room_id: payload.room_id,
type: payload.type,
remote_element: payload.remote_element,
local_element: payload.local_element,
});
}
}
@ -827,12 +807,17 @@ export default class CallHandler {
const mappedRoomId = CallHandler.sharedInstance().roomIdForCall(call);
if (this.getCallForRoom(mappedRoomId)) {
// ignore multiple incoming calls to the same room
console.log(
"Got incoming call for room " + mappedRoomId +
" but there's already a call for this room: ignoring",
);
return;
}
Analytics.trackEvent('voip', 'receiveCall', 'type', call.type);
console.log("Adding call for room ", mappedRoomId);
this.calls.set(mappedRoomId, call)
this.emit(CallHandlerEvent.CallsChanged, this.calls);
this.setCallListeners(call);
// get ready to send encrypted events in the room, so if the user does answer
@ -875,7 +860,6 @@ export default class CallHandler {
const call = this.calls.get(payload.room_id);
call.answer();
this.setCallAudioElement(call);
this.setActiveCallRoomId(payload.room_id);
CountlyAnalytics.instance.trackJoinCall(payload.room_id, call.type === CallType.Video, false);
dis.dispatch({
@ -884,9 +868,43 @@ export default class CallHandler {
});
break;
}
case Action.DialNumber:
this.dialNumber(payload.number);
break;
}
}
private async dialNumber(number: string) {
const results = await this.pstnLookup(number);
if (!results || results.length === 0 || !results[0].userid) {
Modal.createTrackedDialog('', '', ErrorDialog, {
title: _t("Unable to look up phone number"),
description: _t("There was an error looking up the phone number"),
});
return;
}
const userId = results[0].userid;
// Now check to see if this is a virtual user, in which case we should find the
// native user
let nativeUserId;
if (this.getSupportsVirtualRooms()) {
const nativeLookupResults = await this.sipNativeLookup(userId);
const lookupSuccess = nativeLookupResults.length > 0 && nativeLookupResults[0].fields.lookup_success;
nativeUserId = lookupSuccess ? nativeLookupResults[0].userid : userId;
console.log("Looked up " + number + " to " + userId + " and mapped to native user " + nativeUserId);
} else {
nativeUserId = userId;
}
const roomId = await ensureDMExists(MatrixClientPeg.get(), nativeUserId);
dis.dispatch({
action: 'view_room',
room_id: roomId,
});
}
setActiveCallRoomId(activeCallRoomId: string) {
logger.info("Setting call in room " + activeCallRoomId + " active");

View file

@ -16,7 +16,7 @@
import SettingsStore from "./settings/SettingsStore";
import {SettingLevel} from "./settings/SettingLevel";
import {setMatrixCallAudioInput, setMatrixCallAudioOutput, setMatrixCallVideoInput} from "matrix-js-sdk/src/matrix";
import {setMatrixCallAudioInput, setMatrixCallVideoInput} from "matrix-js-sdk/src/matrix";
export default {
hasAnyLabeledDevices: async function() {
@ -50,18 +50,15 @@ export default {
},
loadDevices: function() {
const audioOutDeviceId = SettingsStore.getValue("webrtc_audiooutput");
const audioDeviceId = SettingsStore.getValue("webrtc_audioinput");
const videoDeviceId = SettingsStore.getValue("webrtc_videoinput");
setMatrixCallAudioOutput(audioOutDeviceId);
setMatrixCallAudioInput(audioDeviceId);
setMatrixCallVideoInput(videoDeviceId);
},
setAudioOutput: function(deviceId) {
SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId);
setMatrixCallAudioOutput(deviceId);
},
setAudioInput: function(deviceId) {

View file

@ -28,8 +28,6 @@ import encrypt from "browser-encrypt-attachment";
import extractPngChunks from "png-chunks-extract";
import Spinner from "./components/views/elements/Spinner";
// Polyfill for Canvas.toBlob API using Canvas.toDataURL
import "blueimp-canvas-to-blob";
import { Action } from "./dispatcher/actions";
import CountlyAnalytics from "./CountlyAnalytics";
import {
@ -40,6 +38,7 @@ import {
UploadStartedPayload,
} from "./dispatcher/payloads/UploadPayload";
import {IUpload} from "./models/IUpload";
import { IImageInfo } from "matrix-js-sdk/src/@types/partials";
const MAX_WIDTH = 800;
const MAX_HEIGHT = 600;
@ -208,12 +207,12 @@ function infoForImageFile(matrixClient, roomId, imageFile) {
}
let imageInfo;
return loadImageElement(imageFile).then(function(r) {
return loadImageElement(imageFile).then((r) => {
return createThumbnail(r.img, r.width, r.height, thumbnailType);
}).then(function(result) {
}).then((result) => {
imageInfo = result.info;
return uploadFile(matrixClient, roomId, result.thumbnail);
}).then(function(result) {
}).then((result) => {
imageInfo.thumbnail_url = result.url;
imageInfo.thumbnail_file = result.file;
return imageInfo;
@ -264,12 +263,12 @@ function infoForVideoFile(matrixClient, roomId, videoFile) {
const thumbnailType = "image/jpeg";
let videoInfo;
return loadVideoElement(videoFile).then(function(video) {
return loadVideoElement(videoFile).then((video) => {
return createThumbnail(video, video.videoWidth, video.videoHeight, thumbnailType);
}).then(function(result) {
}).then((result) => {
videoInfo = result.info;
return uploadFile(matrixClient, roomId, result.thumbnail);
}).then(function(result) {
}).then((result) => {
videoInfo.thumbnail_url = result.url;
videoInfo.thumbnail_file = result.file;
return videoInfo;
@ -308,7 +307,12 @@ function readFileAsArrayBuffer(file: File | Blob): Promise<ArrayBuffer> {
* If the file is unencrypted then the object will have a "url" key.
* If the file is encrypted then the object will have a "file" key.
*/
function uploadFile(matrixClient: MatrixClient, roomId: string, file: File | Blob, progressHandler?: any) {
function uploadFile(
matrixClient: MatrixClient,
roomId: string,
file: File | Blob,
progressHandler?: any, // TODO: Types
): Promise<{url?: string, file?: any}> { // TODO: Types
let canceled = false;
if (matrixClient.isRoomEncrypted(roomId)) {
// If the room is encrypted then encrypt the file before uploading it.
@ -355,7 +359,7 @@ function uploadFile(matrixClient: MatrixClient, roomId: string, file: File | Blo
// If the attachment isn't encrypted then include the URL directly.
return {"url": url};
});
promise1.abort = () => {
(promise1 as any).abort = () => {
canceled = true;
MatrixClientPeg.get().cancelUpload(basePromise);
};
@ -367,7 +371,7 @@ export default class ContentMessages {
private inprogress: IUpload[] = [];
private mediaConfig: IMediaConfig = null;
sendStickerContentToRoom(url: string, roomId: string, info: string, text: string, matrixClient: MatrixClient) {
sendStickerContentToRoom(url: string, roomId: string, info: IImageInfo, text: string, matrixClient: MatrixClient) {
const startTime = CountlyAnalytics.getTimestamp();
const prom = MatrixClientPeg.get().sendStickerMessage(roomId, url, info, text).catch((e) => {
console.warn(`Failed to send content with URL ${url} to room ${roomId}`, e);
@ -441,7 +445,7 @@ export default class ContentMessages {
let uploadAll = false;
// Promise to complete before sending next file into room, used for synchronisation of file-sending
// to match the order the files were specified in
let promBefore = Promise.resolve();
let promBefore: Promise<any> = Promise.resolve();
for (let i = 0; i < okFiles.length; ++i) {
const file = okFiles[i];
if (!uploadAll) {

View file

@ -22,13 +22,7 @@ import SdkConfig from './SdkConfig';
import {MatrixClientPeg} from "./MatrixClientPeg";
import {sleep} from "./utils/promise";
import RoomViewStore from "./stores/RoomViewStore";
// polyfill textencoder if necessary
import * as TextEncodingUtf8 from 'text-encoding-utf-8';
let TextEncoder = window.TextEncoder;
if (!TextEncoder) {
TextEncoder = TextEncodingUtf8.TextEncoder;
}
import { Action } from "./dispatcher/actions";
const INACTIVITY_TIME = 20; // seconds
const HEARTBEAT_INTERVAL = 5_000; // ms
@ -265,7 +259,7 @@ interface ICreateRoomEvent extends IEvent {
}
interface IJoinRoomEvent extends IEvent {
key: "join_room";
key: Action.JoinRoom;
dur: number; // how long it took to join (until remote echo)
segmentation: {
room_id: string; // hashed
@ -684,7 +678,9 @@ export default class CountlyAnalytics {
}
private getOrientation = (): Orientation => {
return window.innerWidth > window.innerHeight ? Orientation.Landscape : Orientation.Portrait;
return window.matchMedia("(orientation: landscape)").matches
? Orientation.Landscape
: Orientation.Portrait
};
private reportOrientation = () => {
@ -813,7 +809,9 @@ export default class CountlyAnalytics {
window.addEventListener("mousemove", this.onUserActivity);
window.addEventListener("click", this.onUserActivity);
window.addEventListener("keydown", this.onUserActivity);
window.addEventListener("scroll", this.onUserActivity);
// Using the passive option to not block the main thread
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#improving_scrolling_performance_with_passive_listeners
window.addEventListener("scroll", this.onUserActivity, { passive: true });
this.activityIntervalId = setInterval(() => {
this.inactivityCounter++;
@ -858,7 +856,7 @@ export default class CountlyAnalytics {
}
public trackRoomJoin(startTime: number, roomId: string, type: IJoinRoomEvent["segmentation"]["type"]) {
this.track<IJoinRoomEvent>("join_room", { type }, roomId, {
this.track<IJoinRoomEvent>(Action.JoinRoom, { type }, roomId, {
dur: CountlyAnalytics.getTimestamp() - startTime,
});
}

View file

@ -21,7 +21,6 @@ import MultiInviter from './utils/MultiInviter';
import { _t } from './languageHandler';
import {MatrixClientPeg} from './MatrixClientPeg';
import GroupStore from './stores/GroupStore';
import {allSettled} from "./utils/promise";
import StyledCheckbox from './components/views/elements/StyledCheckbox';
export function showGroupInviteDialog(groupId) {
@ -120,7 +119,7 @@ function _onGroupInviteFinished(groupId, addrs) {
function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
const matrixClient = MatrixClientPeg.get();
const errorList = [];
return allSettled(addrs.map((addr) => {
return Promise.allSettled(addrs.map((addr) => {
return GroupStore
.addRoomToGroup(groupId, addr.address, addRoomsPublicly)
.catch(() => { errorList.push(addr.address); })
@ -148,13 +147,15 @@ function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog(
'Failed to add the following room to the group',
'', ErrorDialog,
{
title: _t(
"Failed to add the following rooms to %(groupId)s:",
{groupId},
),
description: errorList.join(", "),
});
'',
ErrorDialog,
{
title: _t(
"Failed to add the following rooms to %(groupId)s:",
{groupId},
),
description: errorList.join(", "),
},
);
});
}

View file

@ -422,8 +422,12 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
safeBody = sanitizeHtml(formattedBody, sanitizeParams);
if (SettingsStore.getValue("feature_latex_maths")) {
const phtml = cheerio.load(safeBody,
{ _useHtmlParser2: true, decodeEntities: false })
const phtml = cheerio.load(safeBody, {
// @ts-ignore: The `_useHtmlParser2` internal option is the
// simplest way to both parse and render using `htmlparser2`.
_useHtmlParser2: true,
decodeEntities: false,
});
// @ts-ignore - The types for `replaceWith` wrongly expect
// Cheerio instance to be returned.
phtml('div, span[data-mx-maths!=""]').replaceWith(function(i, e) {
@ -431,6 +435,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
AllHtmlEntities.decode(phtml(e).attr('data-mx-maths')),
{
throwOnError: false,
// @ts-ignore - `e` can be an Element, not just a Node
displayMode: e.name == 'div',
output: "htmlAndMathml",
});

View file

@ -163,7 +163,7 @@ export default class IdentityAuthClient {
</div>
),
button: _t("Trust"),
});
});
const [confirmed] = await finished;
if (confirmed) {
// eslint-disable-next-line react-hooks/rules-of-hooks

View file

@ -31,12 +31,12 @@ interface IPasswordFlow {
}
export enum IdentityProviderBrand {
Gitlab = "org.matrix.gitlab",
Github = "org.matrix.github",
Apple = "org.matrix.apple",
Google = "org.matrix.google",
Facebook = "org.matrix.facebook",
Twitter = "org.matrix.twitter",
Gitlab = "gitlab",
Github = "github",
Apple = "apple",
Google = "google",
Facebook = "facebook",
Twitter = "twitter",
}
export interface IIdentityProvider {
@ -48,7 +48,8 @@ export interface IIdentityProvider {
export interface ISSOFlow {
type: "m.login.sso" | "m.login.cas";
"org.matrix.msc2858.identity_providers": IIdentityProvider[]; // Unstable prefix for MSC2858
// eslint-disable-next-line camelcase
identity_providers: IIdentityProvider[];
}
export type LoginFlow = ISSOFlow | IPasswordFlow;

View file

@ -331,6 +331,8 @@ export const Notifier = {
if (!this.isSyncing) return; // don't alert for any messages initially
if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return;
MatrixClientPeg.get().decryptEventIfNeeded(ev);
// If it's an encrypted event and the type is still 'm.room.encrypted',
// it hasn't yet been decrypted, so wait until it is.
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) {

View file

@ -54,7 +54,7 @@ export default class PasswordReset {
return res;
}, function(err) {
if (err.errcode === 'M_THREEPID_NOT_FOUND') {
err.message = _t('This email address was not found');
err.message = _t('This email address was not found');
} else if (err.httpStatus) {
err.message = err.message + ` (Status ${err.httpStatus})`;
}

View file

@ -98,7 +98,7 @@ class Presence {
}
try {
await MatrixClientPeg.get().setPresence(this.state);
await MatrixClientPeg.get().setPresence({presence: this.state});
console.info("Presence:", newState);
} catch (err) {
console.error("Failed to set presence:", err);

View file

@ -1,6 +1,5 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2015-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.
@ -15,35 +14,37 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {MatrixClientPeg} from './MatrixClientPeg';
import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher';
import { EventStatus } from 'matrix-js-sdk/src/models/event';
export default class Resend {
static resendUnsentEvents(room) {
return Promise.all(room.getPendingEvents().filter(function(ev) {
static resendUnsentEvents(room: Room): Promise<void[]> {
return Promise.all(room.getPendingEvents().filter(function(ev: MatrixEvent) {
return ev.status === EventStatus.NOT_SENT;
}).map(function(event) {
}).map(function(event: MatrixEvent) {
return Resend.resend(event);
}));
}
static cancelUnsentEvents(room) {
room.getPendingEvents().filter(function(ev) {
static cancelUnsentEvents(room: Room): void {
room.getPendingEvents().filter(function(ev: MatrixEvent) {
return ev.status === EventStatus.NOT_SENT;
}).forEach(function(event) {
}).forEach(function(event: MatrixEvent) {
Resend.removeFromQueue(event);
});
}
static resend(event) {
static resend(event: MatrixEvent): Promise<void> {
const room = MatrixClientPeg.get().getRoom(event.getRoomId());
return MatrixClientPeg.get().resendEvent(event, room).then(function(res) {
dis.dispatch({
action: 'message_sent',
event: event,
});
}, function(err) {
}, function(err: Error) {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/element-web/issues/3148
console.log('Resend got send failure: ' + err.name + '(' + err + ')');
@ -55,7 +56,7 @@ export default class Resend {
});
}
static removeFromQueue(event) {
static removeFromQueue(event: MatrixEvent): void {
MatrixClientPeg.get().cancelPendingEvent(event);
}
}

View file

@ -1,5 +1,5 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019, 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.
@ -24,12 +24,12 @@ limitations under the License.
* A similar thing could also be achieved via `pushState` with a state object,
* but keeping it separate like this seems easier in case we do want to extend.
*/
const aliasToIDMap = new Map();
const aliasToIDMap = new Map<string, string>();
export function storeRoomAliasInCache(alias, id) {
export function storeRoomAliasInCache(alias: string, id: string): void {
aliasToIDMap.set(alias, id);
}
export function getCachedRoomIDForAlias(alias) {
export function getCachedRoomIDForAlias(alias: string): string {
return aliasToIDMap.get(alias);
}

View file

@ -66,7 +66,7 @@ async function serverSideSearchProcess(term, roomId = undefined) {
highlights: [],
};
return client._processRoomEventsSearch(searchResult, result.response);
return client.processRoomEventsSearch(searchResult, result.response);
}
function compareEvents(a, b) {
@ -131,7 +131,7 @@ async function combinedSearch(searchTerm) {
},
};
const result = client._processRoomEventsSearch(emptyResult, response);
const result = client.processRoomEventsSearch(emptyResult, response);
// Restore our encryption info so we can properly re-verify the events.
restoreEncryptionInfo(result.results);
@ -185,7 +185,7 @@ async function localSearchProcess(searchTerm, roomId = undefined) {
},
};
const processedResult = MatrixClientPeg.get()._processRoomEventsSearch(emptyResult, response);
const processedResult = MatrixClientPeg.get().processRoomEventsSearch(emptyResult, response);
// Restore our encryption info so we can properly re-verify the events.
restoreEncryptionInfo(processedResult.results);
@ -210,7 +210,7 @@ async function localPagination(searchResult) {
},
};
const result = MatrixClientPeg.get()._processRoomEventsSearch(searchResult, response);
const result = MatrixClientPeg.get().processRoomEventsSearch(searchResult, response);
// Restore our encryption info so we can properly re-verify the events.
const newSlice = result.results.slice(Math.max(result.results.length - newResultCount, 0));
@ -520,7 +520,7 @@ async function combinedPagination(searchResult) {
const oldResultCount = searchResult.results ? searchResult.results.length : 0;
// Let the client process the combined result.
const result = client._processRoomEventsSearch(searchResult, response);
const result = client.processRoomEventsSearch(searchResult, response);
// Restore our encryption info so we can properly re-verify the events.
const newResultCount = result.results.length - oldResultCount;

View file

@ -271,7 +271,7 @@ async function onSecretRequested(
}
return key && encodeBase64(key);
} else if (name === "m.megolm_backup.v1") {
const key = await client._crypto.getSessionBackupPrivateKey();
const key = await client.crypto.getSessionBackupPrivateKey();
if (!key) {
console.log(
`session backup key requested by ${deviceId}, but not found in cache`,

View file

@ -38,7 +38,7 @@ import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
import {inviteUsersToRoom} from "./RoomInvite";
import { WidgetType } from "./widgets/WidgetType";
import { Jitsi } from "./widgets/Jitsi";
import { parseFragment as parseHtml } from "parse5";
import { parseFragment as parseHtml, Element as ChildElement } from "parse5";
import BugReportDialog from "./components/views/dialogs/BugReportDialog";
import { ensureDMExists } from "./createRoom";
import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
@ -150,6 +150,10 @@ function success(promise?: Promise<any>) {
return {promise};
}
function successSync(value: any) {
return success(Promise.resolve(value));
}
/* Disable the "unexpected this" error for these commands - all of the run
* functions are called with `this` bound to the Command instance.
*/
@ -160,7 +164,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends the given message as a spoiler'),
runFn: function(roomId, message) {
return success(ContentHelpers.makeHtmlMessage(
return successSync(ContentHelpers.makeHtmlMessage(
message,
`<span data-mx-spoiler>${message}</span>`,
));
@ -176,7 +180,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -189,7 +193,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -202,7 +206,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -215,7 +219,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -224,7 +228,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends a message as plain text, without interpreting it as markdown'),
runFn: function(roomId, messages) {
return success(ContentHelpers.makeTextMessage(messages));
return successSync(ContentHelpers.makeTextMessage(messages));
},
category: CommandCategories.messages,
}),
@ -233,7 +237,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends a message as html, without interpreting it as markdown'),
runFn: function(roomId, messages) {
return success(ContentHelpers.makeHtmlMessage(messages, messages));
return successSync(ContentHelpers.makeHtmlMessage(messages, messages));
},
category: CommandCategories.messages,
}),
@ -856,7 +860,7 @@ export const Commands = [
// some superfast regex over the text so we don't have to.
const embed = parseHtml(widgetUrl);
if (embed && embed.childNodes && embed.childNodes.length === 1) {
const iframe = embed.childNodes[0];
const iframe = embed.childNodes[0] as ChildElement;
if (iframe.tagName.toLowerCase() === 'iframe' && iframe.attrs) {
const srcAttr = iframe.attrs.find(a => a.name === 'src');
console.log("Pulling URL out of iframe (embed code)");
@ -978,7 +982,7 @@ export const Commands = [
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
return success(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
return successSync(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
}),
@ -988,7 +992,7 @@ export const Commands = [
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
return success(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
return successSync(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
}),

View file

@ -36,14 +36,18 @@ export class Service {
}
}
interface Policy {
export interface LocalisedPolicy {
name: string;
url: string;
}
export interface Policy {
// @ts-ignore: No great way to express indexed types together with other keys
version: string;
[lang: string]: {
url: string;
};
[lang: string]: LocalisedPolicy;
}
type Policies = {
export type Policies = {
[policy: string]: Policy,
};
@ -99,7 +103,7 @@ export async function startTermsFlow(
// fetch the set of agreed policy URLs from account data
const currentAcceptedTerms = await MatrixClientPeg.get().getAccountData('m.accepted_terms');
let agreedUrlSet;
let agreedUrlSet: Set<string>;
if (!currentAcceptedTerms || !currentAcceptedTerms.getContent() || !currentAcceptedTerms.getContent().accepted) {
agreedUrlSet = new Set();
} else {

View file

@ -21,153 +21,161 @@ import SettingsStore from "./settings/SettingsStore";
import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList";
import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore";
function textForMemberEvent(ev) {
// These functions are frequently used just to check whether an event has
// any text to display at all. For this reason they return deferred values
// to avoid the expense of looking up translations when they're not needed.
function textForMemberEvent(ev): () => string | null {
// XXX: SYJS-16 "sender is sometimes null for join messages"
const senderName = ev.sender ? ev.sender.name : ev.getSender();
const targetName = ev.target ? ev.target.name : ev.getStateKey();
const prevContent = ev.getPrevContent();
const content = ev.getContent();
const reason = content.reason ? (_t('Reason') + ': ' + content.reason) : '';
const getReason = () => content.reason ? (_t('Reason') + ': ' + content.reason) : '';
switch (content.membership) {
case 'invite': {
const threePidContent = content.third_party_invite;
if (threePidContent) {
if (threePidContent.display_name) {
return _t('%(targetName)s accepted the invitation for %(displayName)s.', {
return () => _t('%(targetName)s accepted the invitation for %(displayName)s.', {
targetName,
displayName: threePidContent.display_name,
});
} else {
return _t('%(targetName)s accepted an invitation.', {targetName});
return () => _t('%(targetName)s accepted an invitation.', {targetName});
}
} else {
return _t('%(senderName)s invited %(targetName)s.', {senderName, targetName});
return () => _t('%(senderName)s invited %(targetName)s.', {senderName, targetName});
}
}
case 'ban':
return _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + reason;
return () => _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + getReason();
case 'join':
if (prevContent && prevContent.membership === 'join') {
if (prevContent.displayname && content.displayname && prevContent.displayname !== content.displayname) {
return _t('%(oldDisplayName)s changed their display name to %(displayName)s.', {
return () => _t('%(oldDisplayName)s changed their display name to %(displayName)s.', {
oldDisplayName: prevContent.displayname,
displayName: content.displayname,
});
} else if (!prevContent.displayname && content.displayname) {
return _t('%(senderName)s set their display name to %(displayName)s.', {
return () => _t('%(senderName)s set their display name to %(displayName)s.', {
senderName: ev.getSender(),
displayName: content.displayname,
});
} else if (prevContent.displayname && !content.displayname) {
return _t('%(senderName)s removed their display name (%(oldDisplayName)s).', {
return () => _t('%(senderName)s removed their display name (%(oldDisplayName)s).', {
senderName,
oldDisplayName: prevContent.displayname,
});
} else if (prevContent.avatar_url && !content.avatar_url) {
return _t('%(senderName)s removed their profile picture.', {senderName});
return () => _t('%(senderName)s removed their profile picture.', {senderName});
} else if (prevContent.avatar_url && content.avatar_url &&
prevContent.avatar_url !== content.avatar_url) {
return _t('%(senderName)s changed their profile picture.', {senderName});
return () => _t('%(senderName)s changed their profile picture.', {senderName});
} else if (!prevContent.avatar_url && content.avatar_url) {
return _t('%(senderName)s set a profile picture.', {senderName});
return () => _t('%(senderName)s set a profile picture.', {senderName});
} else if (SettingsStore.getValue("showHiddenEventsInTimeline")) {
// This is a null rejoin, it will only be visible if the Labs option is enabled
return _t("%(senderName)s made no change.", {senderName});
return () => _t("%(senderName)s made no change.", {senderName});
} else {
return "";
return null;
}
} else {
if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
return _t('%(targetName)s joined the room.', {targetName});
return () => _t('%(targetName)s joined the room.', {targetName});
}
case 'leave':
if (ev.getSender() === ev.getStateKey()) {
if (prevContent.membership === "invite") {
return _t('%(targetName)s rejected the invitation.', {targetName});
return () => _t('%(targetName)s rejected the invitation.', {targetName});
} else {
return _t('%(targetName)s left the room.', {targetName});
return () => _t('%(targetName)s left the room.', {targetName});
}
} else if (prevContent.membership === "ban") {
return _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName});
return () => _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName});
} else if (prevContent.membership === "invite") {
return _t('%(senderName)s withdrew %(targetName)s\'s invitation.', {
return () => _t('%(senderName)s withdrew %(targetName)s\'s invitation.', {
senderName,
targetName,
}) + ' ' + reason;
}) + ' ' + getReason();
} else if (prevContent.membership === "join") {
return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason;
return () => _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + getReason();
} else {
return "";
return null;
}
}
}
function textForTopicEvent(ev) {
function textForTopicEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
return _t('%(senderDisplayName)s changed the topic to "%(topic)s".', {
return () => _t('%(senderDisplayName)s changed the topic to "%(topic)s".', {
senderDisplayName,
topic: ev.getContent().topic,
});
}
function textForRoomNameEvent(ev) {
function textForRoomNameEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
if (!ev.getContent().name || ev.getContent().name.trim().length === 0) {
return _t('%(senderDisplayName)s removed the room name.', {senderDisplayName});
return () => _t('%(senderDisplayName)s removed the room name.', {senderDisplayName});
}
if (ev.getPrevContent().name) {
return _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', {
return () => _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', {
senderDisplayName,
oldRoomName: ev.getPrevContent().name,
newRoomName: ev.getContent().name,
});
}
return _t('%(senderDisplayName)s changed the room name to %(roomName)s.', {
return () => _t('%(senderDisplayName)s changed the room name to %(roomName)s.', {
senderDisplayName,
roomName: ev.getContent().name,
});
}
function textForTombstoneEvent(ev) {
function textForTombstoneEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName});
}
function textForJoinRulesEvent(ev) {
function textForJoinRulesEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
switch (ev.getContent().join_rule) {
case "public":
return _t('%(senderDisplayName)s made the room public to whoever knows the link.', {senderDisplayName});
return () => _t('%(senderDisplayName)s made the room public to whoever knows the link.', {
senderDisplayName,
});
case "invite":
return _t('%(senderDisplayName)s made the room invite only.', {senderDisplayName});
return () => _t('%(senderDisplayName)s made the room invite only.', {
senderDisplayName,
});
default:
// The spec supports "knock" and "private", however nothing implements these.
return _t('%(senderDisplayName)s changed the join rule to %(rule)s', {
return () => _t('%(senderDisplayName)s changed the join rule to %(rule)s', {
senderDisplayName,
rule: ev.getContent().join_rule,
});
}
}
function textForGuestAccessEvent(ev) {
function textForGuestAccessEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
switch (ev.getContent().guest_access) {
case "can_join":
return _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName});
return () => _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName});
case "forbidden":
return _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName});
return () => _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName});
default:
// There's no other options we can expect, however just for safety's sake we'll do this.
return _t('%(senderDisplayName)s changed guest access to %(rule)s', {
return () => _t('%(senderDisplayName)s changed guest access to %(rule)s', {
senderDisplayName,
rule: ev.getContent().guest_access,
});
}
}
function textForRelatedGroupsEvent(ev) {
function textForRelatedGroupsEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
const groups = ev.getContent().groups || [];
const prevGroups = ev.getPrevContent().groups || [];
@ -175,17 +183,17 @@ function textForRelatedGroupsEvent(ev) {
const removed = prevGroups.filter((g) => !groups.includes(g));
if (added.length && !removed.length) {
return _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', {
return () => _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', {
senderDisplayName,
groups: added.join(', '),
});
} else if (!added.length && removed.length) {
return _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', {
return () => _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', {
senderDisplayName,
groups: removed.join(', '),
});
} else if (added.length && removed.length) {
return _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' +
return () => _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' +
'%(oldGroups)s in this room.', {
senderDisplayName,
newGroups: added.join(', '),
@ -193,11 +201,11 @@ function textForRelatedGroupsEvent(ev) {
});
} else {
// Don't bother rendering this change (because there were no changes)
return '';
return null;
}
}
function textForServerACLEvent(ev) {
function textForServerACLEvent(ev): () => string | null {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
const prevContent = ev.getPrevContent();
const current = ev.getContent();
@ -207,11 +215,11 @@ function textForServerACLEvent(ev) {
allow_ip_literals: !(prevContent.allow_ip_literals === false),
};
let text = "";
let getText = null;
if (prev.deny.length === 0 && prev.allow.length === 0) {
text = _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName});
getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName});
} else {
text = _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName});
getText = () => _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName});
}
if (!Array.isArray(current.allow)) {
@ -220,24 +228,27 @@ function textForServerACLEvent(ev) {
// If we know for sure everyone is banned, mark the room as obliterated
if (current.allow.length === 0) {
return text + " " + _t("🎉 All servers are banned from participating! This room can no longer be used.");
return () => getText() + " " +
_t("🎉 All servers are banned from participating! This room can no longer be used.");
}
return text;
return getText;
}
function textForMessageEvent(ev) {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
let message = senderDisplayName + ': ' + ev.getContent().body;
if (ev.getContent().msgtype === "m.emote") {
message = "* " + senderDisplayName + " " + message;
} else if (ev.getContent().msgtype === "m.image") {
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName});
}
return message;
function textForMessageEvent(ev): () => string | null {
return () => {
const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
let message = senderDisplayName + ': ' + ev.getContent().body;
if (ev.getContent().msgtype === "m.emote") {
message = "* " + senderDisplayName + " " + message;
} else if (ev.getContent().msgtype === "m.image") {
message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName});
}
return message;
};
}
function textForCanonicalAliasEvent(ev) {
function textForCanonicalAliasEvent(ev): () => string | null {
const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
const oldAlias = ev.getPrevContent().alias;
const oldAltAliases = ev.getPrevContent().alt_aliases || [];
@ -248,96 +259,100 @@ function textForCanonicalAliasEvent(ev) {
if (!removedAltAliases.length && !addedAltAliases.length) {
if (newAlias) {
return _t('%(senderName)s set the main address for this room to %(address)s.', {
return () => _t('%(senderName)s set the main address for this room to %(address)s.', {
senderName: senderName,
address: ev.getContent().alias,
});
} else if (oldAlias) {
return _t('%(senderName)s removed the main address for this room.', {
return () => _t('%(senderName)s removed the main address for this room.', {
senderName: senderName,
});
}
} else if (newAlias === oldAlias) {
if (addedAltAliases.length && !removedAltAliases.length) {
return _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', {
return () => _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', {
senderName: senderName,
addresses: addedAltAliases.join(", "),
count: addedAltAliases.length,
});
} if (removedAltAliases.length && !addedAltAliases.length) {
return _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', {
return () => _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', {
senderName: senderName,
addresses: removedAltAliases.join(", "),
count: removedAltAliases.length,
});
} if (removedAltAliases.length && addedAltAliases.length) {
return _t('%(senderName)s changed the alternative addresses for this room.', {
return () => _t('%(senderName)s changed the alternative addresses for this room.', {
senderName: senderName,
});
}
} else {
// both alias and alt_aliases where modified
return _t('%(senderName)s changed the main and alternative addresses for this room.', {
return () => _t('%(senderName)s changed the main and alternative addresses for this room.', {
senderName: senderName,
});
}
// in case there is no difference between the two events,
// say something as we can't simply hide the tile from here
return _t('%(senderName)s changed the addresses for this room.', {
return () => _t('%(senderName)s changed the addresses for this room.', {
senderName: senderName,
});
}
function textForCallAnswerEvent(event) {
const senderName = event.sender ? event.sender.name : _t('Someone');
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported;
function textForCallAnswerEvent(event): () => string | null {
return () => {
const senderName = event.sender ? event.sender.name : _t('Someone');
const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)');
return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported;
};
}
function textForCallHangupEvent(event) {
const senderName = event.sender ? event.sender.name : _t('Someone');
function textForCallHangupEvent(event): () => string | null {
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
const eventContent = event.getContent();
let reason = "";
let getReason = () => "";
if (!MatrixClientPeg.get().supportsVoip()) {
reason = _t('(not supported by this browser)');
getReason = () => _t('(not supported by this browser)');
} else if (eventContent.reason) {
if (eventContent.reason === "ice_failed") {
// We couldn't establish a connection at all
reason = _t('(could not connect media)');
getReason = () => _t('(could not connect media)');
} else if (eventContent.reason === "ice_timeout") {
// We established a connection but it died
reason = _t('(connection failed)');
getReason = () => _t('(connection failed)');
} else if (eventContent.reason === "user_media_failed") {
// The other side couldn't open capture devices
reason = _t("(their device couldn't start the camera / microphone)");
getReason = () => _t("(their device couldn't start the camera / microphone)");
} else if (eventContent.reason === "unknown_error") {
// An error code the other side doesn't have a way to express
// (as opposed to an error code they gave but we don't know about,
// in which case we show the error code)
reason = _t("(an error occurred)");
getReason = () => _t("(an error occurred)");
} else if (eventContent.reason === "invite_timeout") {
reason = _t('(no answer)');
getReason = () => _t('(no answer)');
} else if (eventContent.reason === "user hangup" || eventContent.reason === "user_hangup") {
// workaround for https://github.com/vector-im/element-web/issues/5178
// it seems Android randomly sets a reason of "user hangup" which is
// interpreted as an error code :(
// https://github.com/vector-im/riot-android/issues/2623
// Also the correct hangup code as of VoIP v1 (with underscore)
reason = '';
getReason = () => '';
} else {
reason = _t('(unknown failure: %(reason)s)', {reason: eventContent.reason});
getReason = () => _t('(unknown failure: %(reason)s)', {reason: eventContent.reason});
}
}
return _t('%(senderName)s ended the call.', {senderName}) + ' ' + reason;
return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason();
}
function textForCallRejectEvent(event) {
const senderName = event.sender ? event.sender.name : _t('Someone');
return _t('%(senderName)s declined the call.', {senderName});
function textForCallRejectEvent(event): () => string | null {
return () => {
const senderName = event.sender ? event.sender.name : _t('Someone');
return _t('%(senderName)s declined the call.', {senderName});
};
}
function textForCallInviteEvent(event) {
const senderName = event.sender ? event.sender.name : _t('Someone');
function textForCallInviteEvent(event): () => string | null {
const getSenderName = () => event.sender ? event.sender.name : _t('Someone');
// FIXME: Find a better way to determine this from the event?
let isVoice = true;
if (event.getContent().offer && event.getContent().offer.sdp &&
@ -350,48 +365,55 @@ function textForCallInviteEvent(event) {
// can have a hard time translating those strings. In an effort to make translations easier
// and more accurate, we break out the string-based variables to a couple booleans.
if (isVoice && isSupported) {
return _t("%(senderName)s placed a voice call.", {senderName});
return () => _t("%(senderName)s placed a voice call.", {
senderName: getSenderName(),
});
} else if (isVoice && !isSupported) {
return _t("%(senderName)s placed a voice call. (not supported by this browser)", {senderName});
return () => _t("%(senderName)s placed a voice call. (not supported by this browser)", {
senderName: getSenderName(),
});
} else if (!isVoice && isSupported) {
return _t("%(senderName)s placed a video call.", {senderName});
return () => _t("%(senderName)s placed a video call.", {
senderName: getSenderName(),
});
} else if (!isVoice && !isSupported) {
return _t("%(senderName)s placed a video call. (not supported by this browser)", {senderName});
return () => _t("%(senderName)s placed a video call. (not supported by this browser)", {
senderName: getSenderName(),
});
}
}
function textForThreePidInviteEvent(event) {
function textForThreePidInviteEvent(event): () => string | null {
const senderName = event.sender ? event.sender.name : event.getSender();
if (!isValid3pidInvite(event)) {
const targetDisplayName = event.getPrevContent().display_name || _t("Someone");
return _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', {
return () => _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', {
senderName,
targetDisplayName,
targetDisplayName: event.getPrevContent().display_name || _t("Someone"),
});
}
return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {
return () => _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', {
senderName,
targetDisplayName: event.getContent().display_name,
});
}
function textForHistoryVisibilityEvent(event) {
function textForHistoryVisibilityEvent(event): () => string | null {
const senderName = event.sender ? event.sender.name : event.getSender();
switch (event.getContent().history_visibility) {
case 'invited':
return _t('%(senderName)s made future room history visible to all room members, '
return () => _t('%(senderName)s made future room history visible to all room members, '
+ 'from the point they are invited.', {senderName});
case 'joined':
return _t('%(senderName)s made future room history visible to all room members, '
return () => _t('%(senderName)s made future room history visible to all room members, '
+ 'from the point they joined.', {senderName});
case 'shared':
return _t('%(senderName)s made future room history visible to all room members.', {senderName});
return () => _t('%(senderName)s made future room history visible to all room members.', {senderName});
case 'world_readable':
return _t('%(senderName)s made future room history visible to anyone.', {senderName});
return () => _t('%(senderName)s made future room history visible to anyone.', {senderName});
default:
return _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', {
return () => _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', {
senderName,
visibility: event.getContent().history_visibility,
});
@ -399,11 +421,11 @@ function textForHistoryVisibilityEvent(event) {
}
// Currently will only display a change if a user's power level is changed
function textForPowerEvent(event) {
function textForPowerEvent(event): () => string | null {
const senderName = event.sender ? event.sender.name : event.getSender();
if (!event.getPrevContent() || !event.getPrevContent().users ||
!event.getContent() || !event.getContent().users) {
return '';
return null;
}
const userDefault = event.getContent().users_default || 0;
// Construct set of userIds
@ -418,38 +440,38 @@ function textForPowerEvent(event) {
if (users.indexOf(userId) === -1) users.push(userId);
},
);
const diff = [];
// XXX: This is also surely broken for i18n
const diffs = [];
users.forEach((userId) => {
// Previous power level
const from = event.getPrevContent().users[userId];
// Current power level
const to = event.getContent().users[userId];
if (to !== from) {
diff.push(
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
userId,
fromPowerLevel: Roles.textualPowerLevel(from, userDefault),
toPowerLevel: Roles.textualPowerLevel(to, userDefault),
}),
);
diffs.push({ userId, from, to });
}
});
if (!diff.length) {
return '';
if (!diffs.length) {
return null;
}
return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
// XXX: This is also surely broken for i18n
return () => _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', {
senderName,
powerLevelDiffText: diff.join(", "),
powerLevelDiffText: diffs.map(diff =>
_t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', {
userId: diff.userId,
fromPowerLevel: Roles.textualPowerLevel(diff.from, userDefault),
toPowerLevel: Roles.textualPowerLevel(diff.to, userDefault),
}),
).join(", "),
});
}
function textForPinnedEvent(event) {
function textForPinnedEvent(event): () => string | null {
const senderName = event.sender ? event.sender.name : event.getSender();
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
return () => _t("%(senderName)s changed the pinned messages for the room.", {senderName});
}
function textForWidgetEvent(event) {
function textForWidgetEvent(event): () => string | null {
const senderName = event.getSender();
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent();
const {name, type, url} = event.getContent() || {};
@ -464,27 +486,27 @@ function textForWidgetEvent(event) {
// equivalent to that condition.
if (url) {
if (prevUrl) {
return _t('%(widgetName)s widget modified by %(senderName)s', {
return () => _t('%(widgetName)s widget modified by %(senderName)s', {
widgetName, senderName,
});
} else {
return _t('%(widgetName)s widget added by %(senderName)s', {
return () => _t('%(widgetName)s widget added by %(senderName)s', {
widgetName, senderName,
});
}
} else {
return _t('%(widgetName)s widget removed by %(senderName)s', {
return () => _t('%(widgetName)s widget removed by %(senderName)s', {
widgetName, senderName,
});
}
}
function textForWidgetLayoutEvent(event) {
function textForWidgetLayoutEvent(event): () => string | null {
const senderName = event.sender?.name || event.getSender();
return _t("%(senderName)s has updated the widget layout", {senderName});
return () => _t("%(senderName)s has updated the widget layout", {senderName});
}
function textForMjolnirEvent(event) {
function textForMjolnirEvent(event): () => string | null {
const senderName = event.getSender();
const {entity: prevEntity} = event.getPrevContent();
const {entity, recommendation, reason} = event.getContent();
@ -492,80 +514,90 @@ function textForMjolnirEvent(event) {
// Rule removed
if (!entity) {
if (USER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s removed the rule banning users matching %(glob)s",
return () => _t("%(senderName)s removed the rule banning users matching %(glob)s",
{senderName, glob: prevEntity});
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s removed the rule banning rooms matching %(glob)s",
return () => _t("%(senderName)s removed the rule banning rooms matching %(glob)s",
{senderName, glob: prevEntity});
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s removed the rule banning servers matching %(glob)s",
return () => _t("%(senderName)s removed the rule banning servers matching %(glob)s",
{senderName, glob: prevEntity});
}
// Unknown type. We'll say something, but we shouldn't end up here.
return _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity});
return () => _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity});
}
// Invalid rule
if (!recommendation || !reason) return _t(`%(senderName)s updated an invalid ban rule`, {senderName});
if (!recommendation || !reason) return () => _t(`%(senderName)s updated an invalid ban rule`, {senderName});
// Rule updated
if (entity === prevEntity) {
if (USER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
}
// Unknown type. We'll say something but we shouldn't end up here.
return _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
}
// New rule
if (!prevEntity) {
if (USER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
}
// Unknown type. We'll say something but we shouldn't end up here.
return _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s",
return () => _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s",
{senderName, glob: entity, reason});
}
// else the entity !== prevEntity - count as a removal & add
if (USER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
return () => _t(
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason});
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
} else if (ROOM_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
return () => _t(
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason});
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
} else if (SERVER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
return () => _t(
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason});
{senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
}
// Unknown type. We'll say something but we shouldn't end up here.
return _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " +
return () => _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " +
"for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason});
}
const handlers = {
interface IHandlers {
[type: string]: (ev: any) => (() => string | null);
}
const handlers: IHandlers = {
'm.room.message': textForMessageEvent,
'm.call.invite': textForCallInviteEvent,
'm.call.answer': textForCallAnswerEvent,
@ -573,7 +605,7 @@ const handlers = {
'm.call.reject': textForCallRejectEvent,
};
const stateHandlers = {
const stateHandlers: IHandlers = {
'm.room.canonical_alias': textForCanonicalAliasEvent,
'm.room.name': textForRoomNameEvent,
'm.room.topic': textForTopicEvent,
@ -598,8 +630,12 @@ for (const evType of ALL_RULE_TYPES) {
stateHandlers[evType] = textForMjolnirEvent;
}
export function textForEvent(ev) {
export function hasText(ev): boolean {
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
if (handler) return handler(ev);
return '';
return Boolean(handler?.(ev));
}
export function textForEvent(ev): string {
const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()];
return handler?.(ev)?.() || '';
}

View file

@ -40,6 +40,8 @@ export function eventTriggersUnreadCount(ev) {
return false;
} else if (ev.getType() == 'm.room.server_acl') {
return false;
} else if (ev.isRedacted()) {
return false;
}
return haveTileForEvent(ev);
}

View file

@ -24,7 +24,9 @@ import { Room } from 'matrix-js-sdk/src/models/room';
// is sip virtual: there could be others in the future.
export default class VoipUserMapper {
private virtualRoomIdCache = new Set<string>();
// We store mappings of virtual -> native room IDs here until the local echo for the
// account data arrives.
private virtualToNativeRoomIdCache = new Map<string, string>();
public static sharedInstance(): VoipUserMapper {
if (window.mxVoipUserMapper === undefined) window.mxVoipUserMapper = new VoipUserMapper();
@ -33,7 +35,7 @@ export default class VoipUserMapper {
private async userToVirtualUser(userId: string): Promise<string> {
const results = await CallHandler.sharedInstance().sipVirtualLookup(userId);
if (results.length === 0) return null;
if (results.length === 0 || !results[0].fields.lookup_success) return null;
return results[0].userid;
}
@ -49,10 +51,20 @@ export default class VoipUserMapper {
native_room: roomId,
});
this.virtualToNativeRoomIdCache.set(virtualRoomId, roomId);
return virtualRoomId;
}
public nativeRoomForVirtualRoom(roomId: string): string {
const cachedNativeRoomId = this.virtualToNativeRoomIdCache.get(roomId);
if (cachedNativeRoomId) {
console.log(
"Returning native room ID " + cachedNativeRoomId + " for virtual room ID " + roomId + " from cache",
);
return cachedNativeRoomId;
}
const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
if (!virtualRoom) return null;
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
@ -67,7 +79,7 @@ export default class VoipUserMapper {
public isVirtualRoom(room: Room): boolean {
if (this.nativeRoomForVirtualRoom(room.roomId)) return true;
if (this.virtualRoomIdCache.has(room.roomId)) return true;
if (this.virtualToNativeRoomIdCache.has(room.roomId)) return true;
// also look in the create event for the claimed native room ID, which is the only
// way we can recognise a virtual room we've created when it first arrives down
@ -82,14 +94,14 @@ export default class VoipUserMapper {
return Boolean(claimedNativeRoomId);
}
public async onNewInvitedRoom(invitedRoom: Room) {
public async onNewInvitedRoom(invitedRoom: Room): Promise<void> {
if (!CallHandler.sharedInstance().getSupportsVirtualRooms()) return;
const inviterId = invitedRoom.getDMInviter();
console.log(`Checking virtual-ness of room ID ${invitedRoom.roomId}, invited by ${inviterId}`);
const result = await CallHandler.sharedInstance().sipNativeLookup(inviterId);
if (result.length === 0) {
return true;
return;
}
if (result[0].fields.is_virtual) {
@ -110,7 +122,7 @@ export default class VoipUserMapper {
// also put this room in the virtual room ID cache so isVirtualRoom return the right answer
// in however long it takes for the echo of setAccountData to come down the sync
this.virtualRoomIdCache.add(invitedRoom.roomId);
this.virtualToNativeRoomIdCache.set(invitedRoom.roomId, nativeRoom.roomId);
}
}
}

View file

@ -167,7 +167,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
const onKeyDownHandler = useCallback((ev) => {
let handled = false;
// Don't interfere with input default keydown behaviour
if (handleHomeEnd && ev.target.tagName !== "INPUT") {
if (handleHomeEnd && ev.target.tagName !== "INPUT" && ev.target.tagName !== "TEXTAREA") {
// check if we actually have any items
switch (ev.key) {
case Key.HOME:

View file

@ -1,5 +1,5 @@
/*
Copyright 2017 New Vector Ltd
Copyright 2017, 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.
@ -14,7 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher/dispatcher';
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import dis from "../dispatcher/dispatcher";
import {ActionPayload} from "../dispatcher/payloads";
// TODO: migrate from sync_state to MatrixActions.sync so that more js-sdk events
// become dispatches in the same place.
@ -27,7 +33,7 @@ import dis from '../dispatcher/dispatcher';
* @param {string} prevState the previous sync state.
* @returns {Object} an action of type MatrixActions.sync.
*/
function createSyncAction(matrixClient, state, prevState) {
function createSyncAction(matrixClient: MatrixClient, state: string, prevState: string): ActionPayload {
return {
action: 'MatrixActions.sync',
state,
@ -53,7 +59,7 @@ function createSyncAction(matrixClient, state, prevState) {
* @param {MatrixEvent} accountDataEvent the account data event.
* @returns {AccountDataAction} an action of type MatrixActions.accountData.
*/
function createAccountDataAction(matrixClient, accountDataEvent) {
function createAccountDataAction(matrixClient: MatrixClient, accountDataEvent: MatrixEvent): ActionPayload {
return {
action: 'MatrixActions.accountData',
event: accountDataEvent,
@ -81,7 +87,11 @@ function createAccountDataAction(matrixClient, accountDataEvent) {
* @param {Room} room the room where account data was changed
* @returns {RoomAccountDataAction} an action of type MatrixActions.Room.accountData.
*/
function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
function createRoomAccountDataAction(
matrixClient: MatrixClient,
accountDataEvent: MatrixEvent,
room: Room,
): ActionPayload {
return {
action: 'MatrixActions.Room.accountData',
event: accountDataEvent,
@ -106,7 +116,7 @@ function createRoomAccountDataAction(matrixClient, accountDataEvent, room) {
* @param {Room} room the Room that was stored.
* @returns {RoomAction} an action of type `MatrixActions.Room`.
*/
function createRoomAction(matrixClient, room) {
function createRoomAction(matrixClient: MatrixClient, room: Room): ActionPayload {
return { action: 'MatrixActions.Room', room };
}
@ -127,7 +137,7 @@ function createRoomAction(matrixClient, room) {
* @param {Room} room the Room whose tags were changed.
* @returns {RoomTagsAction} an action of type `MatrixActions.Room.tags`.
*/
function createRoomTagsAction(matrixClient, roomTagsEvent, room) {
function createRoomTagsAction(matrixClient: MatrixClient, roomTagsEvent: MatrixEvent, room: Room): ActionPayload {
return { action: 'MatrixActions.Room.tags', room };
}
@ -140,7 +150,7 @@ function createRoomTagsAction(matrixClient, roomTagsEvent, room) {
* @param {Room} room the room the receipt happened in.
* @returns {Object} an action of type MatrixActions.Room.receipt.
*/
function createRoomReceiptAction(matrixClient, event, room) {
function createRoomReceiptAction(matrixClient: MatrixClient, event: MatrixEvent, room: Room): ActionPayload {
return {
action: 'MatrixActions.Room.receipt',
event,
@ -178,7 +188,17 @@ function createRoomReceiptAction(matrixClient, event, room) {
* @param {EventTimeline} data.timeline the timeline being altered.
* @returns {RoomTimelineAction} an action of type `MatrixActions.Room.timeline`.
*/
function createRoomTimelineAction(matrixClient, timelineEvent, room, toStartOfTimeline, removed, data) {
function createRoomTimelineAction(
matrixClient: MatrixClient,
timelineEvent: MatrixEvent,
room: Room,
toStartOfTimeline: boolean,
removed: boolean,
data: {
liveEvent: boolean;
timeline: EventTimeline;
},
): ActionPayload {
return {
action: 'MatrixActions.Room.timeline',
event: timelineEvent,
@ -208,8 +228,13 @@ function createRoomTimelineAction(matrixClient, timelineEvent, room, toStartOfTi
* @param {string} oldMembership the previous membership, can be null.
* @returns {RoomMembershipAction} an action of type `MatrixActions.Room.myMembership`.
*/
function createSelfMembershipAction(matrixClient, room, membership, oldMembership) {
return { action: 'MatrixActions.Room.myMembership', room, membership, oldMembership};
function createSelfMembershipAction(
matrixClient: MatrixClient,
room: Room,
membership: string,
oldMembership: string,
): ActionPayload {
return { action: 'MatrixActions.Room.myMembership', room, membership, oldMembership };
}
/**
@ -228,61 +253,65 @@ function createSelfMembershipAction(matrixClient, room, membership, oldMembershi
* @param {MatrixEvent} event the matrix event that was decrypted.
* @returns {EventDecryptedAction} an action of type `MatrixActions.Event.decrypted`.
*/
function createEventDecryptedAction(matrixClient, event) {
function createEventDecryptedAction(matrixClient: MatrixClient, event: MatrixEvent): ActionPayload {
return { action: 'MatrixActions.Event.decrypted', event };
}
type Listener = () => void;
type ActionCreator = (matrixClient: MatrixClient, ...args: any) => ActionPayload;
// A list of callbacks to call to unregister all listeners added
let matrixClientListenersStop: Listener[] = [];
/**
* Start listening to events of type eventName on matrixClient and when they are emitted,
* dispatch an action created by the actionCreator function.
* @param {MatrixClient} matrixClient a MatrixClient to register a listener with.
* @param {string} eventName the event to listen to on MatrixClient.
* @param {function} actionCreator a function that should return an action to dispatch
* when given the MatrixClient as an argument as well as
* arguments emitted in the MatrixClient event.
*/
function addMatrixClientListener(matrixClient: MatrixClient, eventName: string, actionCreator: ActionCreator): void {
const listener: Listener = (...args) => {
const payload = actionCreator(matrixClient, ...args);
if (payload) {
dis.dispatch(payload, true);
}
};
matrixClient.on(eventName, listener);
matrixClientListenersStop.push(() => {
matrixClient.removeListener(eventName, listener);
});
}
/**
* This object is responsible for dispatching actions when certain events are emitted by
* the given MatrixClient.
*/
export default {
// A list of callbacks to call to unregister all listeners added
_matrixClientListenersStop: [],
/**
* Start listening to certain events from the MatrixClient and dispatch actions when
* they are emitted.
* @param {MatrixClient} matrixClient the MatrixClient to listen to events from
*/
start(matrixClient) {
this._addMatrixClientListener(matrixClient, 'sync', createSyncAction);
this._addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
this._addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
this._addMatrixClientListener(matrixClient, 'Room', createRoomAction);
this._addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
this._addMatrixClientListener(matrixClient, 'Room.receipt', createRoomReceiptAction);
this._addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
this._addMatrixClientListener(matrixClient, 'Room.myMembership', createSelfMembershipAction);
this._addMatrixClientListener(matrixClient, 'Event.decrypted', createEventDecryptedAction);
},
/**
* Start listening to events of type eventName on matrixClient and when they are emitted,
* dispatch an action created by the actionCreator function.
* @param {MatrixClient} matrixClient a MatrixClient to register a listener with.
* @param {string} eventName the event to listen to on MatrixClient.
* @param {function} actionCreator a function that should return an action to dispatch
* when given the MatrixClient as an argument as well as
* arguments emitted in the MatrixClient event.
*/
_addMatrixClientListener(matrixClient, eventName, actionCreator) {
const listener = (...args) => {
const payload = actionCreator(matrixClient, ...args);
if (payload) {
dis.dispatch(payload, true);
}
};
matrixClient.on(eventName, listener);
this._matrixClientListenersStop.push(() => {
matrixClient.removeListener(eventName, listener);
});
start(matrixClient: MatrixClient) {
addMatrixClientListener(matrixClient, 'sync', createSyncAction);
addMatrixClientListener(matrixClient, 'accountData', createAccountDataAction);
addMatrixClientListener(matrixClient, 'Room.accountData', createRoomAccountDataAction);
addMatrixClientListener(matrixClient, 'Room', createRoomAction);
addMatrixClientListener(matrixClient, 'Room.tags', createRoomTagsAction);
addMatrixClientListener(matrixClient, 'Room.receipt', createRoomReceiptAction);
addMatrixClientListener(matrixClient, 'Room.timeline', createRoomTimelineAction);
addMatrixClientListener(matrixClient, 'Room.myMembership', createSelfMembershipAction);
addMatrixClientListener(matrixClient, 'Event.decrypted', createEventDecryptedAction);
},
/**
* Stop listening to events.
*/
stop() {
this._matrixClientListenersStop.forEach((stopListener) => stopListener());
matrixClientListenersStop.forEach((stopListener) => stopListener());
matrixClientListenersStop = [];
},
};

View file

@ -310,7 +310,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
<p>{_t(
"Please enter your Security Phrase a second time to confirm.",
"Enter your Security Phrase a second time to confirm it.",
)}</p>
<div className="mx_CreateKeyBackupDialog_primaryContainer">
<div className="mx_CreateKeyBackupDialog_passPhraseContainer">
@ -498,9 +498,9 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
title={this._titleForPhase(this.state.phase)}
hasCancel={[PHASE_PASSPHRASE, PHASE_DONE].includes(this.state.phase)}
>
<div>
{content}
</div>
<div>
{content}
</div>
</BaseDialog>
);
}

View file

@ -647,7 +647,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
}
return <form onSubmit={this._onPassPhraseConfirmNextClick}>
<p>{_t(
"Enter your recovery passphrase a second time to confirm it.",
"Enter your Security Phrase a second time to confirm it.",
)}</p>
<div className="mx_CreateSecretStorageDialog_passPhraseContainer">
<Field
@ -655,7 +655,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
onChange={this._onPassPhraseConfirmChange}
value={this.state.passPhraseConfirm}
className="mx_CreateSecretStorageDialog_passPhraseField"
label={_t("Confirm your recovery passphrase")}
label={_t("Confirm your Security Phrase")}
autoFocus={true}
autoComplete="new-password"
/>
@ -856,9 +856,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
hasCancel={this.props.hasCancel && [PHASE_PASSPHRASE].includes(this.state.phase)}
fixedWidth={false}
>
<div>
{content}
</div>
<div>
{content}
</div>
</BaseDialog>
);
}

View file

@ -170,8 +170,11 @@ export default class ExportE2eKeysDialog extends React.Component {
</div>
</div>
<div className='mx_Dialog_buttons'>
<input className='mx_Dialog_primary' type='submit' value={_t('Export')}
disabled={disableForm}
<input
className='mx_Dialog_primary'
type='submit'
value={_t('Export')}
disabled={disableForm}
/>
<button onClick={this._onCancelClick} disabled={disableForm}>
{ _t("Cancel") }

View file

@ -140,36 +140,36 @@ export default class ImportE2eKeysDialog extends React.Component {
</div>
<div className='mx_E2eKeysDialog_inputTable'>
<div className='mx_E2eKeysDialog_inputRow'>
<div className='mx_E2eKeysDialog_inputLabel'>
<label htmlFor='importFile'>
{ _t("File to import") }
</label>
</div>
<div className='mx_E2eKeysDialog_inputCell'>
<input
ref={this._file}
id='importFile'
type='file'
autoFocus={true}
onChange={this._onFormChange}
disabled={disableForm} />
</div>
<div className='mx_E2eKeysDialog_inputLabel'>
<label htmlFor='importFile'>
{ _t("File to import") }
</label>
</div>
<div className='mx_E2eKeysDialog_inputCell'>
<input
ref={this._file}
id='importFile'
type='file'
autoFocus={true}
onChange={this._onFormChange}
disabled={disableForm} />
</div>
</div>
<div className='mx_E2eKeysDialog_inputRow'>
<div className='mx_E2eKeysDialog_inputLabel'>
<label htmlFor='passphrase'>
{ _t("Enter passphrase") }
</label>
</div>
<div className='mx_E2eKeysDialog_inputCell'>
<input
ref={this._passphrase}
id='passphrase'
size='64'
type='password'
onChange={this._onFormChange}
disabled={disableForm} />
</div>
<div className='mx_E2eKeysDialog_inputLabel'>
<label htmlFor='passphrase'>
{ _t("Enter passphrase") }
</label>
</div>
<div className='mx_E2eKeysDialog_inputCell'>
<input
ref={this._passphrase}
id='passphrase'
size='64'
type='password'
onChange={this._onFormChange}
disabled={disableForm} />
</div>
</div>
</div>
</div>

View file

@ -93,7 +93,12 @@ export default class AutocompleteProvider {
};
}
async getCompletions(query: string, selection: ISelectionRange, force = false): Promise<ICompletion[]> {
async getCompletions(
query: string,
selection: ISelectionRange,
force = false,
limit = -1,
): Promise<ICompletion[]> {
return [];
}

Some files were not shown because too many files have changed in this diff Show more