diff --git a/.babelrc b/.babelrc index fc5bd1788f..3fb847ad18 100644 --- a/.babelrc +++ b/.babelrc @@ -1,4 +1,21 @@ { - "presets": ["react", "es2015", "es2016"], - "plugins": ["transform-class-properties", "transform-object-rest-spread", "transform-async-to-bluebird", "transform-runtime", "add-module-exports", "syntax-dynamic-import"] + "presets": [ + "react", + "es2015", + "es2016" + ], + "plugins": [ + [ + "transform-builtin-extend", + { + "globals": ["Error"] + } + ], + "transform-class-properties", + "transform-object-rest-spread", + "transform-async-to-bluebird", + "transform-runtime", + "add-module-exports", + "syntax-dynamic-import" + ] } diff --git a/.buildkite/pipeline.yaml b/.buildkite/pipeline.yaml new file mode 100644 index 0000000000..946e417676 --- /dev/null +++ b/.buildkite/pipeline.yaml @@ -0,0 +1,91 @@ +steps: + - label: ":eslint: Lint" + command: + - "yarn install" + - "yarn lintwithexclusions" + - "yarn stylelint" + plugins: + - docker#v3.0.1: + image: "node:10" + + - label: ":chains: End-to-End Tests" + agents: + # We use a medium sized instance instead of the normal small ones because + # e2e tests otherwise take +-8min + queue: "medium" + command: + # TODO: Remove hacky chmod for BuildKite + - "echo '--- Setup'" + - "chmod +x ./scripts/ci/*.sh" + - "chmod +x ./scripts/*" + - "echo '--- Install js-sdk'" + - "./scripts/ci/install-deps.sh" + - "./scripts/ci/end-to-end-tests.sh" + plugins: + - docker#v3.0.1: + image: "matrixdotorg/riotweb-ci-e2etests-env:latest" + propagate-environment: true + + - label: ":karma: Tests" + agents: + # We use a medium sized instance instead of the normal small ones because + # webpack loves to gorge itself on resources. + queue: "medium" + command: + # Install chrome + - "echo '--- Installing Chrome'" + - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -" + - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'" + - "apt-get update" + - "apt-get install -y google-chrome-stable" + # Run tests + # TODO: Remove hacky chmod for BuildKite + - "chmod +x ./scripts/ci/*.sh" + - "chmod +x ./scripts/*" + - "echo '--- Installing Dependencies'" + - "./scripts/ci/install-deps.sh" + - "echo '+++ Running Tests'" + - "./scripts/ci/unit-tests.sh" + env: + CHROME_BIN: "/usr/bin/google-chrome-stable" + plugins: + - docker#v3.0.1: + image: "node:10" + propagate-environment: true + + - label: "🔧 Riot Tests" + agents: + # We use a medium sized instance instead of the normal small ones because + # webpack loves to gorge itself on resources. + queue: "medium" + command: + # Install chrome + - "echo '--- Installing Chrome'" + - "wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -" + - "sh -c 'echo \"deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google.list'" + - "apt-get update" + - "apt-get install -y google-chrome-stable" + # Run tests + # TODO: Remove hacky chmod for BuildKite + - "chmod +x ./scripts/ci/*.sh" + - "chmod +x ./scripts/*" + - "echo '--- Installing Dependencies'" + - "./scripts/ci/install-deps.sh" + - "echo '+++ Running Tests'" + - "./scripts/ci/riot-unit-tests.sh" + env: + CHROME_BIN: "/usr/bin/google-chrome-stable" + plugins: + - docker#v3.0.1: + image: "node:10" + propagate-environment: true + + - wait + + - label: "🐴 Trigger riot-web" + trigger: "riot-web" + branches: "develop" + build: + branch: "develop" + message: "[react-sdk] ${BUILDKITE_MESSAGE}" + async: true diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 2ccf83489c..3636a5e563 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -4,13 +4,11 @@ src/component-index.js src/components/structures/BottomLeftMenu.js src/components/structures/CreateRoom.js src/components/structures/MessagePanel.js -src/components/structures/NotificationPanel.js src/components/structures/RoomDirectory.js src/components/structures/RoomStatusBar.js src/components/structures/RoomView.js src/components/structures/ScrollPanel.js src/components/structures/SearchBox.js -src/components/structures/TimelinePanel.js src/components/structures/UploadBar.js src/components/views/avatars/BaseAvatar.js src/components/views/avatars/MemberAvatar.js @@ -18,7 +16,6 @@ src/components/views/create_room/RoomAlias.js src/components/views/dialogs/DeactivateAccountDialog.js src/components/views/dialogs/SetPasswordDialog.js src/components/views/dialogs/UnknownDeviceDialog.js -src/components/views/directory/NetworkDropdown.js src/components/views/elements/AddressSelector.js src/components/views/elements/DirectorySearchBox.js src/components/views/elements/ImageView.js @@ -31,7 +28,6 @@ src/components/views/globals/UpdateCheckBar.js src/components/views/messages/MFileBody.js src/components/views/messages/RoomAvatarEvent.js src/components/views/messages/TextualBody.js -src/components/views/room_settings/AliasSettings.js src/components/views/room_settings/ColorSettings.js src/components/views/rooms/Autocomplete.js src/components/views/rooms/AuxPanel.js @@ -44,19 +40,16 @@ src/components/views/rooms/MessageComposer.js src/components/views/rooms/PinnedEventTile.js src/components/views/rooms/RoomList.js src/components/views/rooms/RoomPreviewBar.js -src/components/views/rooms/RoomSettings.js src/components/views/rooms/SearchableEntityList.js src/components/views/rooms/SearchBar.js src/components/views/rooms/SearchResultTile.js src/components/views/rooms/TopUnreadMessagesBar.js src/components/views/rooms/UserTile.js -src/components/views/settings/AddPhoneNumber.js src/components/views/settings/ChangeAvatar.js src/components/views/settings/ChangePassword.js src/components/views/settings/DevicesPanel.js src/components/views/settings/IntegrationsManager.js src/components/views/settings/Notifications.js -src/ContentMessages.js src/GroupAddressPicker.js src/HtmlUtils.js src/ImageUtils.js @@ -64,7 +57,6 @@ src/languageHandler.js src/linkify-matrix.js src/Markdown.js src/MatrixClientPeg.js -src/Modal.js src/notifications/ContentRules.js src/notifications/PushRuleVectorState.js src/notifications/VectorPushRulesDefinitions.js diff --git a/.eslintrc.js b/.eslintrc.js index ec48f6b2ff..fdf0bb351e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,6 +15,9 @@ module.exports = { "flowtype", "babel" ], + globals: { + LANGUAGES_FILE: "readonly", + }, env: { es6: true, }, @@ -91,13 +94,13 @@ module.exports = { // to JSX. ignorePattern: '^\\s*<', ignoreComments: true, + ignoreRegExpLiterals: true, code: 120, }], "valid-jsdoc": ["warn"], "new-cap": ["warn"], "key-spacing": ["warn"], "prefer-const": ["warn"], - "arrow-parens": "off", // crashes currently: https://github.com/eslint/eslint/issues/6274 "generator-star-spacing": "off", diff --git a/.gitignore b/.gitignore index 6acf1b565a..33e8bfc7ac 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,15 @@ -npm-debug.log +/.npmrc +/*.log +package-lock.json /node_modules /lib -# version file and tarball created by 'npm pack' +# version file and tarball created by `npm pack` / `yarn pack` /git-revision.txt /matrix-react-sdk-*.tgz -# test reports created by karma -/karma-reports - /.idea /src/component-index.js .DS_Store - -# https://github.com/vector-im/riot-web/issues/7083 -package-lock.json diff --git a/.stylelintrc.js b/.stylelintrc.js new file mode 100644 index 0000000000..fc00b643a0 --- /dev/null +++ b/.stylelintrc.js @@ -0,0 +1,15 @@ +module.exports = { + "extends": "stylelint-config-standard", + "rules": { + "indentation": 4, + "comment-empty-line-before": null, + "declaration-empty-line-before": null, + "length-zero-no-unit": null, + "rule-empty-line-before": null, + "color-hex-length": null, + "max-empty-lines": null, + "number-no-trailing-zeros": null, + "number-leading-zero": null, + "selector-list-comma-newline-after": null, + } +} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ebb772fd0d..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -# we need trusty for the chrome addon -dist: trusty - -# we don't need sudo, so can run in a container, which makes startup much -# quicker. -# -# unfortunately we do temporarily require sudo as a workaround for -# https://github.com/travis-ci/travis-ci/issues/8836 -sudo: required - -language: node_js -node_js: - - node # Latest stable version of nodejs. -addons: - chrome: stable -install: - - ./scripts/travis/install-deps.sh -matrix: - include: - - name: Linting Checks - script: - # run the linter, but exclude any files known to have errors or warnings. - - npm run lintwithexclusions - # - name: End-to-End Tests - # if: branch = develop - # install: - # - sudo apt-get install build-essential python2.7-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev - # script: - # - ./scripts/travis/end-to-end-tests.sh - - name: Unit Tests - script: - - ./scripts/travis/unit-tests.sh - - name: Riot-web Unit Tests - script: - - ./scripts/travis/riot-unit-tests.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 7293b00d1b..4119af8136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,1335 @@ +Changes in [1.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.0) (2019-05-29) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.2.0-rc.1...v1.2.0) + + * COLR font check fixes for release + [\#3041](https://github.com/matrix-org/matrix-react-sdk/pull/3041) + * Revert "Make the timeline less noisy for screen readers (mk II) #3019" for + release + [\#3036](https://github.com/matrix-org/matrix-react-sdk/pull/3036) + * Override font for usercontent download link for release + [\#3037](https://github.com/matrix-org/matrix-react-sdk/pull/3037) + +Changes in [1.2.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.2.0-rc.1) (2019-05-23) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.1.2...v1.2.0-rc.1) + + * Update from Weblate + [\#3023](https://github.com/matrix-org/matrix-react-sdk/pull/3023) + * Use the correct line-height for bold emoji + [\#3022](https://github.com/matrix-org/matrix-react-sdk/pull/3022) + * Make the timeline less noisy for screen readers (mk II) + [\#3019](https://github.com/matrix-org/matrix-react-sdk/pull/3019) + * Label message edit field as such for screen readers + [\#3020](https://github.com/matrix-org/matrix-react-sdk/pull/3020) + * Move checkmark to the front of key backup message + [\#3014](https://github.com/matrix-org/matrix-react-sdk/pull/3014) + * Revert "Make the timeline less noisy for screen readers" + [\#3017](https://github.com/matrix-org/matrix-react-sdk/pull/3017) + * Translate scroll movement if the deltaX is the same as the threshold + [\#3016](https://github.com/matrix-org/matrix-react-sdk/pull/3016) + * Make the timeline less noisy for screen readers + [\#3007](https://github.com/matrix-org/matrix-react-sdk/pull/3007) + * Windows emoji tweaks + [\#3015](https://github.com/matrix-org/matrix-react-sdk/pull/3015) + * Message editing: update link previews after editing + [\#3004](https://github.com/matrix-org/matrix-react-sdk/pull/3004) + * js-sdk interactive auth now sends email token + [\#3010](https://github.com/matrix-org/matrix-react-sdk/pull/3010) + * remove SBIX font and fallback to native emoji + [\#3011](https://github.com/matrix-org/matrix-react-sdk/pull/3011) + * Update from Weblate + [\#3012](https://github.com/matrix-org/matrix-react-sdk/pull/3012) + * load twemoji dynamically as colr or sbix; fix monospace + [\#3008](https://github.com/matrix-org/matrix-react-sdk/pull/3008) + * Guard against null rooms in `onEventDecrypted` + [\#3009](https://github.com/matrix-org/matrix-react-sdk/pull/3009) + * Only show reactions in main message timeline + [\#3005](https://github.com/matrix-org/matrix-react-sdk/pull/3005) + * Add voice labels for quick add room buttons + [\#3006](https://github.com/matrix-org/matrix-react-sdk/pull/3006) + * Update TopLeftMenu for accessibility: Keyboard shortcut, reduced screen + reader noise + [\#2994](https://github.com/matrix-org/matrix-react-sdk/pull/2994) + * Remove reacted with text when shortcode missing + [\#3003](https://github.com/matrix-org/matrix-react-sdk/pull/3003) + * Fixup: also change editor margin when last event and buttons are not + overlaying + [\#3002](https://github.com/matrix-org/matrix-react-sdk/pull/3002) + * Message editing: render avatars for pills in the editor + [\#2997](https://github.com/matrix-org/matrix-react-sdk/pull/2997) + * Replace emojione with twemoji + emojibase + [\#2995](https://github.com/matrix-org/matrix-react-sdk/pull/2995) + * Hide WhoIsTyping component if the MessagePanel is shaped e.g file grid + [\#3000](https://github.com/matrix-org/matrix-react-sdk/pull/3000) + * Close copy tooltip in edge cases correctly + [\#2999](https://github.com/matrix-org/matrix-react-sdk/pull/2999) + * Limit reaction sender tooltip to 6 people + [\#2998](https://github.com/matrix-org/matrix-react-sdk/pull/2998) + * Message editing: apply design + [\#2996](https://github.com/matrix-org/matrix-react-sdk/pull/2996) + * Add debug feature to show hidden events in timeline + [\#2993](https://github.com/matrix-org/matrix-react-sdk/pull/2993) + * Mute screen readers over reactions + [\#2986](https://github.com/matrix-org/matrix-react-sdk/pull/2986) + * Fix not being able to edit already edited messages + [\#2992](https://github.com/matrix-org/matrix-react-sdk/pull/2992) + * Add a basic tooltip showing who reacted + [\#2991](https://github.com/matrix-org/matrix-react-sdk/pull/2991) + * Message editing: show (edited) marker on edited messages, with tooltip + [\#2990](https://github.com/matrix-org/matrix-react-sdk/pull/2990) + * Update from Weblate + [\#2989](https://github.com/matrix-org/matrix-react-sdk/pull/2989) + * Message editing: only allow editing of text messages + [\#2988](https://github.com/matrix-org/matrix-react-sdk/pull/2988) + * Message editing: shift+enter for newline, enter to send + [\#2987](https://github.com/matrix-org/matrix-react-sdk/pull/2987) + * Apply Flex voodoo for devtools send event dialog + [\#2985](https://github.com/matrix-org/matrix-react-sdk/pull/2985) + * Fix some source strings noticed as incorrect by translators + [\#2984](https://github.com/matrix-org/matrix-react-sdk/pull/2984) + * Message editing: fix some bugs in cursor behaviour + [\#2983](https://github.com/matrix-org/matrix-react-sdk/pull/2983) + * Message editing: local echo & back-pagination + [\#2982](https://github.com/matrix-org/matrix-react-sdk/pull/2982) + * Listen for removed relations + [\#2981](https://github.com/matrix-org/matrix-react-sdk/pull/2981) + * Update from Weblate + [\#2980](https://github.com/matrix-org/matrix-react-sdk/pull/2980) + * Use `getRelation` helper + [\#2977](https://github.com/matrix-org/matrix-react-sdk/pull/2977) + * Add tooltips to rotate and close buttons in ImageView (#9686) + [\#2979](https://github.com/matrix-org/matrix-react-sdk/pull/2979) + * Message editing: smaller fixes + [\#2978](https://github.com/matrix-org/matrix-react-sdk/pull/2978) + * Message editing: adjust to js-sdk changes of marking original event as + replaced + [\#2973](https://github.com/matrix-org/matrix-react-sdk/pull/2973) + * Fix Single Sign-on + [\#2974](https://github.com/matrix-org/matrix-react-sdk/pull/2974) + * Initial support for editing messages + [\#2952](https://github.com/matrix-org/matrix-react-sdk/pull/2952) + * Check permission to invite before showing invite buttons/disable them + [\#2957](https://github.com/matrix-org/matrix-react-sdk/pull/2957) + * Support a backup room ID in PermalinkCreator + [\#2963](https://github.com/matrix-org/matrix-react-sdk/pull/2963) + * Always thumbnail for GIFs + [\#2962](https://github.com/matrix-org/matrix-react-sdk/pull/2962) + * Fix registration with email + [\#2967](https://github.com/matrix-org/matrix-react-sdk/pull/2967) + * Add configuration flag to disable minimum password requirements + [\#2947](https://github.com/matrix-org/matrix-react-sdk/pull/2947) + * Send and undo reaction events + [\#2954](https://github.com/matrix-org/matrix-react-sdk/pull/2954) + * Fix bug where email was not required where it shouldn't have been + [\#2961](https://github.com/matrix-org/matrix-react-sdk/pull/2961) + * add /rainbow and /rainbowme Slash Commands + [\#2958](https://github.com/matrix-org/matrix-react-sdk/pull/2958) + * Fix invite via MemberInfo + [\#2956](https://github.com/matrix-org/matrix-react-sdk/pull/2956) + * Close Room Settings upon Leave Room + [\#2955](https://github.com/matrix-org/matrix-react-sdk/pull/2955) + * Command to change avatar for a single room, including upload of mxc res + [\#2953](https://github.com/matrix-org/matrix-react-sdk/pull/2953) + * Add View Servers in Room to Devtools + [\#2804](https://github.com/matrix-org/matrix-react-sdk/pull/2804) + * Update 'Rooms' import RoomView.js file + [\#2951](https://github.com/matrix-org/matrix-react-sdk/pull/2951) + * Extract `ReactionDimension` out of `MessageActionBar` + [\#2950](https://github.com/matrix-org/matrix-react-sdk/pull/2950) + * Always default to the registration form + [\#2942](https://github.com/matrix-org/matrix-react-sdk/pull/2942) + * Check for `room` in all `Room.timeline*` handlers + [\#2945](https://github.com/matrix-org/matrix-react-sdk/pull/2945) + * Remove the karma junit reporter + [\#2944](https://github.com/matrix-org/matrix-react-sdk/pull/2944) + * yarn upgrade + [\#2943](https://github.com/matrix-org/matrix-react-sdk/pull/2943) + * Support changing options for .m.rule.tombstone push rule + [\#2798](https://github.com/matrix-org/matrix-react-sdk/pull/2798) + * Remove timeline explosion rageshake prompt + [\#2939](https://github.com/matrix-org/matrix-react-sdk/pull/2939) + * Add existing reactions below message + [\#2940](https://github.com/matrix-org/matrix-react-sdk/pull/2940) + * Fix lint errors in TimelinePanel + [\#2938](https://github.com/matrix-org/matrix-react-sdk/pull/2938) + * Add primary reactions to action bar + [\#2937](https://github.com/matrix-org/matrix-react-sdk/pull/2937) + +Changes in [1.1.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.1.2) (2019-05-15) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.1.1...v1.1.2) + + * Always thumbnail for GIFs + [\#2976](https://github.com/matrix-org/matrix-react-sdk/pull/2976) + * Fix Single Sign-on + [\#2975](https://github.com/matrix-org/matrix-react-sdk/pull/2975) + +Changes in [1.1.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.1.1) (2019-05-14) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.1.0...v1.1.1) + + * Fix registration with email + [\#2970](https://github.com/matrix-org/matrix-react-sdk/pull/2970) + * Fix bug where email was not required where it shouldn't have been + [\#2969](https://github.com/matrix-org/matrix-react-sdk/pull/2969) + +Changes in [1.1.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.1.0) (2019-05-07) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.1.0-rc.1...v1.1.0) + + * Relax password requirements to score of 3 out of 4 + [\#2949](https://github.com/matrix-org/matrix-react-sdk/pull/2949) + * Restore access to message quote option on first click + [\#2948](https://github.com/matrix-org/matrix-react-sdk/pull/2948) + * Check for `room` in all `Room.timeline*` handlers + [\#2946](https://github.com/matrix-org/matrix-react-sdk/pull/2946) + +Changes in [1.1.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.1.0-rc.1) (2019-04-30) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.7...v1.1.0-rc.1) + + * Add important info to new preview bar + [\#2936](https://github.com/matrix-org/matrix-react-sdk/pull/2936) + * Add a message action bar + [\#2935](https://github.com/matrix-org/matrix-react-sdk/pull/2935) + * Trigger riot-web build + [\#2934](https://github.com/matrix-org/matrix-react-sdk/pull/2934) + * Input validation tooltips for registration + [\#2933](https://github.com/matrix-org/matrix-react-sdk/pull/2933) + * Also say "Connect ..." on remaining key backup buttons + [\#2931](https://github.com/matrix-org/matrix-react-sdk/pull/2931) + * Mark a few CSS classes as not selectable + [\#2929](https://github.com/matrix-org/matrix-react-sdk/pull/2929) + * Cleanup message composer render() method + [\#2883](https://github.com/matrix-org/matrix-react-sdk/pull/2883) + * Redesigned room preview bar + [\#2925](https://github.com/matrix-org/matrix-react-sdk/pull/2925) + * Prevent user pills containing only emoji from embiggening + [\#2907](https://github.com/matrix-org/matrix-react-sdk/pull/2907) + * Make alt-enter insert new line on macOS + [\#2923](https://github.com/matrix-org/matrix-react-sdk/pull/2923) + * Test `defaultServerName` before showing it on forgot password + [\#2924](https://github.com/matrix-org/matrix-react-sdk/pull/2924) + * Add a function to append/overwrite objects in the config on the fly + [\#2922](https://github.com/matrix-org/matrix-react-sdk/pull/2922) + * use SdkConfig brand name instead of static "Riot" + [\#2921](https://github.com/matrix-org/matrix-react-sdk/pull/2921) + * Use dedicated permalink creators in search results with multiple rooms + [\#2898](https://github.com/matrix-org/matrix-react-sdk/pull/2898) + * Clarify that use backup means restore + [\#2917](https://github.com/matrix-org/matrix-react-sdk/pull/2917) + * Fix key backup status when missing device + [\#2919](https://github.com/matrix-org/matrix-react-sdk/pull/2919) + * Ensure `` tags appear bold for all browsers + [\#2918](https://github.com/matrix-org/matrix-react-sdk/pull/2918) + * Add a link in room settings to get at the tombstoned room if it exists + [\#2908](https://github.com/matrix-org/matrix-react-sdk/pull/2908) + * Add a generic error page element for startup errors + [\#2915](https://github.com/matrix-org/matrix-react-sdk/pull/2915) + * Add strings for js-sdk autodiscovery errors + [\#2916](https://github.com/matrix-org/matrix-react-sdk/pull/2916) + * Focus the composer view on file upload + [\#2914](https://github.com/matrix-org/matrix-react-sdk/pull/2914) + * use medium agent for e2e tests + [\#2911](https://github.com/matrix-org/matrix-react-sdk/pull/2911) + * adjust prop in HeaderButton + [\#2912](https://github.com/matrix-org/matrix-react-sdk/pull/2912) + * Remove breadcrumb scroll tolerances and use sensible defaults + [\#2913](https://github.com/matrix-org/matrix-react-sdk/pull/2913) + * Fix having to click the member list button twice to show it after having + changed room. + [\#2906](https://github.com/matrix-org/matrix-react-sdk/pull/2906) + * Add period to the end of upgrade notice + [\#2909](https://github.com/matrix-org/matrix-react-sdk/pull/2909) + * Remove duplicate space in credits + [\#2889](https://github.com/matrix-org/matrix-react-sdk/pull/2889) + * Handle M_UNSUPPORTED_ROOM_VERSION in invites and room creation + [\#2905](https://github.com/matrix-org/matrix-react-sdk/pull/2905) + * Re-enable E2E tests + [\#2867](https://github.com/matrix-org/matrix-react-sdk/pull/2867) + * Remove BottomLeftMenu and supporting bits + [\#2903](https://github.com/matrix-org/matrix-react-sdk/pull/2903) + * Fix for retina thumbnails being massive + [\#2439](https://github.com/matrix-org/matrix-react-sdk/pull/2439) + * Send breadcrumb updates only when they change + [\#2894](https://github.com/matrix-org/matrix-react-sdk/pull/2894) + * Add some tolerances to breadcrumb scrolling + [\#2892](https://github.com/matrix-org/matrix-react-sdk/pull/2892) + * Fix validation to avoid `undefined` class on fields + [\#2902](https://github.com/matrix-org/matrix-react-sdk/pull/2902) + * Always return a client from onRegistered + [\#2895](https://github.com/matrix-org/matrix-react-sdk/pull/2895) + * Fix room upgrade warnings popping up in upgraded rooms + [\#2897](https://github.com/matrix-org/matrix-react-sdk/pull/2897) + * Fix style lint errors & enable on CI + [\#2901](https://github.com/matrix-org/matrix-react-sdk/pull/2901) + * Add stylelint + [\#2900](https://github.com/matrix-org/matrix-react-sdk/pull/2900) + * Key backup: Handle case where your onw sig is invalid + [\#2899](https://github.com/matrix-org/matrix-react-sdk/pull/2899) + * Simplify settings dialog CSS + [\#2891](https://github.com/matrix-org/matrix-react-sdk/pull/2891) + * Fix upload cancel in e2e rooms + [\#2893](https://github.com/matrix-org/matrix-react-sdk/pull/2893) + * Set E2E room status to warning when crypto is disabled + [\#2890](https://github.com/matrix-org/matrix-react-sdk/pull/2890) + * Move SettingsDialog width override to fixedWidth + [\#2888](https://github.com/matrix-org/matrix-react-sdk/pull/2888) + * Prevent the permalink creator from causing cascading failure + [\#2882](https://github.com/matrix-org/matrix-react-sdk/pull/2882) + * Don't include all networks by default in the room directory + [\#2881](https://github.com/matrix-org/matrix-react-sdk/pull/2881) + * Fix fixed width dialogs + [\#2886](https://github.com/matrix-org/matrix-react-sdk/pull/2886) + * Fix settings dialog layout + [\#2885](https://github.com/matrix-org/matrix-react-sdk/pull/2885) + * Update from Weblate + [\#2884](https://github.com/matrix-org/matrix-react-sdk/pull/2884) + * Design tweaks to dialogs + [\#2868](https://github.com/matrix-org/matrix-react-sdk/pull/2868) + * Remove 'try the app' link from login + [\#2880](https://github.com/matrix-org/matrix-react-sdk/pull/2880) + * Track store failures after startup + [\#2870](https://github.com/matrix-org/matrix-react-sdk/pull/2870) + * Translate vertical scrolling to horizontal movement in breadcrumbs + [\#2877](https://github.com/matrix-org/matrix-react-sdk/pull/2877) + * Add telemetry for breadcrumbs and have the setting apply without refresh + [\#2873](https://github.com/matrix-org/matrix-react-sdk/pull/2873) + * Fix a few bugs introduced in file upload rework + [\#2879](https://github.com/matrix-org/matrix-react-sdk/pull/2879) + * Sync breadcrumb rooms through account data + [\#2875](https://github.com/matrix-org/matrix-react-sdk/pull/2875) + * Scroll breadcrumbs to the left when they change + [\#2878](https://github.com/matrix-org/matrix-react-sdk/pull/2878) + * Add an indicator to show a room is a direct chat in breadcrumbs + [\#2874](https://github.com/matrix-org/matrix-react-sdk/pull/2874) + * Use the most recent version of the room in breadcrumbs + [\#2872](https://github.com/matrix-org/matrix-react-sdk/pull/2872) + * Autohide the scrollbar on breadcrumbs + [\#2876](https://github.com/matrix-org/matrix-react-sdk/pull/2876) + * Ensure the page URL is redacted before tracking analytics events + [\#2871](https://github.com/matrix-org/matrix-react-sdk/pull/2871) + * fix NPE for rooms with redacted tombstones + [\#2869](https://github.com/matrix-org/matrix-react-sdk/pull/2869) + * Don't re-init the stickerpicker unless something actually changes + [\#2862](https://github.com/matrix-org/matrix-react-sdk/pull/2862) + * Add option to rotate images + [\#2855](https://github.com/matrix-org/matrix-react-sdk/pull/2855) + * Add badges to breadcrumb rooms + [\#2861](https://github.com/matrix-org/matrix-react-sdk/pull/2861) + * Include the current power level in the selector + [\#2866](https://github.com/matrix-org/matrix-react-sdk/pull/2866) + * Apply 50% opacity to left breadcrumbs + [\#2860](https://github.com/matrix-org/matrix-react-sdk/pull/2860) + * Small scroll fixes + [\#2865](https://github.com/matrix-org/matrix-react-sdk/pull/2865) + * Put the stickerpicker below dialogs + [\#2863](https://github.com/matrix-org/matrix-react-sdk/pull/2863) + * Logging tweaks + [\#2864](https://github.com/matrix-org/matrix-react-sdk/pull/2864) + * Implement redesigned upload confirmation screens + [\#2858](https://github.com/matrix-org/matrix-react-sdk/pull/2858) + * Use Field component in bug report dialog + [\#2859](https://github.com/matrix-org/matrix-react-sdk/pull/2859) + * Notify user when crypto data is missing + [\#2841](https://github.com/matrix-org/matrix-react-sdk/pull/2841) + * Update from Weblate + [\#2857](https://github.com/matrix-org/matrix-react-sdk/pull/2857) + * Download PDFs as blobs to avoid empty grey screens + [\#2847](https://github.com/matrix-org/matrix-react-sdk/pull/2847) + * Set title attribute on images in lightbox + [\#2848](https://github.com/matrix-org/matrix-react-sdk/pull/2848) + * Add MemberInfo for 3pid invites and support revoking those invites + [\#2843](https://github.com/matrix-org/matrix-react-sdk/pull/2843) + * round scrollTop upwards to prevent never detecting bottom + [\#2846](https://github.com/matrix-org/matrix-react-sdk/pull/2846) + * Notifier is how singleton is known outside of this module + [\#2845](https://github.com/matrix-org/matrix-react-sdk/pull/2845) + * Delay `Notifier` check until we have push rules + [\#2844](https://github.com/matrix-org/matrix-react-sdk/pull/2844) + * BACAT Scrolling + [\#2842](https://github.com/matrix-org/matrix-react-sdk/pull/2842) + * Handle storage fallback cases in consistency check + [\#2840](https://github.com/matrix-org/matrix-react-sdk/pull/2840) + * Handle all the segments of a v3 event ID + [\#2827](https://github.com/matrix-org/matrix-react-sdk/pull/2827) + * Add custom tooltips and scrolling to breadcrumbs + [\#2839](https://github.com/matrix-org/matrix-react-sdk/pull/2839) + * Check if the message panel is at the end of the timeline on init + [\#2829](https://github.com/matrix-org/matrix-react-sdk/pull/2829) + * Persist breadcrumb state between sessions + [\#2837](https://github.com/matrix-org/matrix-react-sdk/pull/2837) + * Always append the current room to the breadcrumbs + [\#2838](https://github.com/matrix-org/matrix-react-sdk/pull/2838) + * Alert the user to unread notifications in prior versions of rooms + [\#2831](https://github.com/matrix-org/matrix-react-sdk/pull/2831) + * Filter out upgraded rooms from autocomplete results + [\#2830](https://github.com/matrix-org/matrix-react-sdk/pull/2830) + +Changes in [1.0.7](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.7) (2019-04-08) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.6...v1.0.7) + + * Hotfix: bump js-sdk to 1.0.4, see https://github.com/matrix-org/matrix-js-sdk/releases/tag/v1.0.4 + +Changes in [1.0.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.6) (2019-04-01) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.6-rc.1...v1.0.6) + + * Handle storage fallback cases in consistency check + [\#2853](https://github.com/matrix-org/matrix-react-sdk/pull/2853) + * Set title attribute on images in lightbox + [\#2852](https://github.com/matrix-org/matrix-react-sdk/pull/2852) + * Download PDFs as blobs to avoid empty grey screens + [\#2851](https://github.com/matrix-org/matrix-react-sdk/pull/2851) + * Add MemberInfo for 3pid invites and support revoking those invites + [\#2850](https://github.com/matrix-org/matrix-react-sdk/pull/2850) + +Changes in [1.0.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.6-rc.1) (2019-03-27) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.5...v1.0.6-rc.1) + + * Catch errors when checking IndexedDB + [\#2836](https://github.com/matrix-org/matrix-react-sdk/pull/2836) + * Remove noreferrer on widget pop-out + [\#2835](https://github.com/matrix-org/matrix-react-sdk/pull/2835) + * Rework room directory so that new room is always available + [\#2834](https://github.com/matrix-org/matrix-react-sdk/pull/2834) + * Send telemetry about storage consistency + [\#2832](https://github.com/matrix-org/matrix-react-sdk/pull/2832) + * Widget OpenID reauth implementation + [\#2781](https://github.com/matrix-org/matrix-react-sdk/pull/2781) + * Log results of basic storage consistency check + [\#2826](https://github.com/matrix-org/matrix-react-sdk/pull/2826) + * Clarify devices affected by notification settings + [\#2828](https://github.com/matrix-org/matrix-react-sdk/pull/2828) + * Add a command for creating custom widgets without an integration manager + [\#2824](https://github.com/matrix-org/matrix-react-sdk/pull/2824) + * Minimize stickerpicker when the title is clicked + [\#2822](https://github.com/matrix-org/matrix-react-sdk/pull/2822) + * Add blocks around homeserver and identity server urls + [\#2825](https://github.com/matrix-org/matrix-react-sdk/pull/2825) + * Fixed drop shadow for tooltip. + [\#2815](https://github.com/matrix-org/matrix-react-sdk/pull/2815) + * Ask the user for debug logs when the timeline explodes + [\#2820](https://github.com/matrix-org/matrix-react-sdk/pull/2820) + * Fix typo preventing users from adding more widgets easily + [\#2823](https://github.com/matrix-org/matrix-react-sdk/pull/2823) + * Attach an onChange listener to the room's blacklist devices option + [\#2817](https://github.com/matrix-org/matrix-react-sdk/pull/2817) + * Use leaveRoomChain when leaving a room + [\#2818](https://github.com/matrix-org/matrix-react-sdk/pull/2818) + * Fix bug with NetworkList dropdown + [\#2821](https://github.com/matrix-org/matrix-react-sdk/pull/2821) + * Trim the logging for URL previews + [\#2816](https://github.com/matrix-org/matrix-react-sdk/pull/2816) + * Explicitly create `cryptoStore` in React SDK + [\#2814](https://github.com/matrix-org/matrix-react-sdk/pull/2814) + * Change to new consistent name for `MemoryStore` + [\#2812](https://github.com/matrix-org/matrix-react-sdk/pull/2812) + * Use medium agents for the more resource intensive builds + [\#2813](https://github.com/matrix-org/matrix-react-sdk/pull/2813) + * Add log grouping to buildkite + [\#2810](https://github.com/matrix-org/matrix-react-sdk/pull/2810) + * Switch to `git` protocol for CI dependencies + [\#2809](https://github.com/matrix-org/matrix-react-sdk/pull/2809) + * Go back to using mainine velocity + [\#2808](https://github.com/matrix-org/matrix-react-sdk/pull/2808) + * Warn that members won't be autojoined to upgraded rooms + [\#2796](https://github.com/matrix-org/matrix-react-sdk/pull/2796) + * Support CI for matching branches on forks + [\#2807](https://github.com/matrix-org/matrix-react-sdk/pull/2807) + * Discard old sticker picker when the URL changes + [\#2801](https://github.com/matrix-org/matrix-react-sdk/pull/2801) + * Reload widget messaging when widgets reload + [\#2799](https://github.com/matrix-org/matrix-react-sdk/pull/2799) + * Don't show calculated room name in room settings name input field + [\#2806](https://github.com/matrix-org/matrix-react-sdk/pull/2806) + * Disable big emoji for m.emote messages as it looks weird + [\#2805](https://github.com/matrix-org/matrix-react-sdk/pull/2805) + * Remove Edge from browser support statements + [\#2803](https://github.com/matrix-org/matrix-react-sdk/pull/2803) + * Update from Weblate + [\#2802](https://github.com/matrix-org/matrix-react-sdk/pull/2802) + * Really fix tag panel + [\#2800](https://github.com/matrix-org/matrix-react-sdk/pull/2800) + * Update CompatibilityPage to match officially supported browsers + [\#2793](https://github.com/matrix-org/matrix-react-sdk/pull/2793) + * Use Buildkite for CI + [\#2788](https://github.com/matrix-org/matrix-react-sdk/pull/2788) + * Fix CSS syntax errors preventing offline member opacity from working + [\#2794](https://github.com/matrix-org/matrix-react-sdk/pull/2794) + * Make the EntityTile chevron a masked SVG for theming + [\#2795](https://github.com/matrix-org/matrix-react-sdk/pull/2795) + * Remove refs from `RegistrationForm` + [\#2791](https://github.com/matrix-org/matrix-react-sdk/pull/2791) + * Fix initial letter avatar vertical offset in Firefox + [\#2792](https://github.com/matrix-org/matrix-react-sdk/pull/2792) + * Fix the custom tag panel + [\#2797](https://github.com/matrix-org/matrix-react-sdk/pull/2797) + * Ensure freshly invited members don't count towards the alone warning + [\#2786](https://github.com/matrix-org/matrix-react-sdk/pull/2786) + * Fix 'forgot password' warning to represent the reality of e2ee + [\#2787](https://github.com/matrix-org/matrix-react-sdk/pull/2787) + * Restore `Field` value getter for `RegistrationForm` + [\#2790](https://github.com/matrix-org/matrix-react-sdk/pull/2790) + * Initial portions of support for Field validation + [\#2780](https://github.com/matrix-org/matrix-react-sdk/pull/2780) + +Changes in [1.0.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.5) (2019-03-21) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.4...v1.0.5) + + * Hotfix: disable typing notifs jumping prevention for now + [\#2811](https://github.com/matrix-org/matrix-react-sdk/pull/2811) + +Changes in [1.0.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.4) (2019-03-18) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.4-rc.1...v1.0.4) + + * No changes since rc.1 + +Changes in [1.0.4-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.4-rc.1) (2019-03-13) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.3...v1.0.4-rc.1) + + * Update from Weblate + [\#2785](https://github.com/matrix-org/matrix-react-sdk/pull/2785) + * Remove padlock click handler to show unknown devices + [\#2784](https://github.com/matrix-org/matrix-react-sdk/pull/2784) + * Use modern Yarn version on Travis CI + [\#2783](https://github.com/matrix-org/matrix-react-sdk/pull/2783) + * Add versioning to integration manager API /register and /account calls + [\#2782](https://github.com/matrix-org/matrix-react-sdk/pull/2782) + * Ensure scalar_token is valid before opening integrations manager + [\#2777](https://github.com/matrix-org/matrix-react-sdk/pull/2777) + * Switch to `yarn` for dependency management + [\#2773](https://github.com/matrix-org/matrix-react-sdk/pull/2773) + * Use a distinct color for selected autocomplete items + [\#2778](https://github.com/matrix-org/matrix-react-sdk/pull/2778) + * Provide an escape from the registration process + [\#2775](https://github.com/matrix-org/matrix-react-sdk/pull/2775) + * Fix instantly sending RRs + [\#2770](https://github.com/matrix-org/matrix-react-sdk/pull/2770) + * Fix simple header counters to correctly handle zero, take two + [\#2776](https://github.com/matrix-org/matrix-react-sdk/pull/2776) + * Fix sticky hover state by listening for hover on the document + [\#2764](https://github.com/matrix-org/matrix-react-sdk/pull/2764) + * Fix header counters to correctly handle zero + [\#2772](https://github.com/matrix-org/matrix-react-sdk/pull/2772) + * Pass correct args when creating event permalink in context menu + [\#2774](https://github.com/matrix-org/matrix-react-sdk/pull/2774) + * Update from Weblate + [\#2771](https://github.com/matrix-org/matrix-react-sdk/pull/2771) + * Scroll investigation changes + [\#2766](https://github.com/matrix-org/matrix-react-sdk/pull/2766) + * Ability to bulk accept all invites (and fix rejecting all invites) + [\#2757](https://github.com/matrix-org/matrix-react-sdk/pull/2757) + * Don't trample over existing sessions when verifying email addresses + [\#2768](https://github.com/matrix-org/matrix-react-sdk/pull/2768) + * Misc fixes to StatusMessageContextMenu + [\#2767](https://github.com/matrix-org/matrix-react-sdk/pull/2767) + * Fix erroneously sending RRs, pt1. + [\#2769](https://github.com/matrix-org/matrix-react-sdk/pull/2769) + * Tweak country dropdown for redesign + [\#2765](https://github.com/matrix-org/matrix-react-sdk/pull/2765) + * Prevent space characters from stopping big emoji messages + [\#2745](https://github.com/matrix-org/matrix-react-sdk/pull/2745) + * Tweak auth components when dark theme is default + [\#2763](https://github.com/matrix-org/matrix-react-sdk/pull/2763) + * Move country dropdown inside field in Settings + [\#2756](https://github.com/matrix-org/matrix-react-sdk/pull/2756) + * npm audit fix --force + [\#2758](https://github.com/matrix-org/matrix-react-sdk/pull/2758) + * Use Field component in auth flows + [\#2749](https://github.com/matrix-org/matrix-react-sdk/pull/2749) + * Add /shrug command + [\#2660](https://github.com/matrix-org/matrix-react-sdk/pull/2660) + * Support linking to hosting providers + [\#2748](https://github.com/matrix-org/matrix-react-sdk/pull/2748) + * Get confirmation before enabling encryption + [\#2728](https://github.com/matrix-org/matrix-react-sdk/pull/2728) + * clear min-height for typing notifs when the timeline viewport changes size + [\#2747](https://github.com/matrix-org/matrix-react-sdk/pull/2747) + * Prevent flair pushing timeline downwards + [\#2746](https://github.com/matrix-org/matrix-react-sdk/pull/2746) + * Include tag name when warning about rooms running off lists + [\#2741](https://github.com/matrix-org/matrix-react-sdk/pull/2741) + * Reorganise props handling in Field + [\#2740](https://github.com/matrix-org/matrix-react-sdk/pull/2740) + * Limit Field label to size of input + [\#2742](https://github.com/matrix-org/matrix-react-sdk/pull/2742) + * Don't loop forever if you demote yourself via Enter on a PowerSelector + [\#2743](https://github.com/matrix-org/matrix-react-sdk/pull/2743) + * Check if users are already in the room before inviting them + [\#2734](https://github.com/matrix-org/matrix-react-sdk/pull/2734) + * Don't duplicate direct chats from other tags + [\#2735](https://github.com/matrix-org/matrix-react-sdk/pull/2735) + * Fix media device selectors not updating + [\#2739](https://github.com/matrix-org/matrix-react-sdk/pull/2739) + * Fix Room Directory custom homeserver entry not showing properly + [\#2708](https://github.com/matrix-org/matrix-react-sdk/pull/2708) + * Add missing permalinkCreator prop + [\#2729](https://github.com/matrix-org/matrix-react-sdk/pull/2729) + * Patch users not existing when opening settings + [\#2738](https://github.com/matrix-org/matrix-react-sdk/pull/2738) + * Use a mask for the continuation icon + [\#2737](https://github.com/matrix-org/matrix-react-sdk/pull/2737) + * Always insert rooms into lists when they get lost + [\#2736](https://github.com/matrix-org/matrix-react-sdk/pull/2736) + * Convert objects and such to usable strings in rageshake + [\#2732](https://github.com/matrix-org/matrix-react-sdk/pull/2732) + * Support stacking dialogs to prevent unmounting + [\#2724](https://github.com/matrix-org/matrix-react-sdk/pull/2724) + * Convert PowerSelector to use mxField instead + [\#2725](https://github.com/matrix-org/matrix-react-sdk/pull/2725) + * Don't re-enable the save button after saving profiles + [\#2726](https://github.com/matrix-org/matrix-react-sdk/pull/2726) + * Support multiple email pushers and remove the legacy UserSettingsStore + [\#2727](https://github.com/matrix-org/matrix-react-sdk/pull/2727) + * Bring back #528 fix as it still seems broken on OSX + [\#2731](https://github.com/matrix-org/matrix-react-sdk/pull/2731) + * Fix margin on e2e icon in member panel + [\#2721](https://github.com/matrix-org/matrix-react-sdk/pull/2721) + * Improved scrolling & pagination + [\#2676](https://github.com/matrix-org/matrix-react-sdk/pull/2676) + * Nudge karma to 3.1.2 + [\#2719](https://github.com/matrix-org/matrix-react-sdk/pull/2719) + * Stop listening to account data when the stickerpicker is unmounted + [\#2723](https://github.com/matrix-org/matrix-react-sdk/pull/2723) + +Changes in [1.0.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.3) (2019-03-06) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.2...v1.0.3) + + * Don't duplicate direct chats from other tags + [\#2762](https://github.com/matrix-org/matrix-react-sdk/pull/2762) + * Include tag name when warning about rooms running off lists + [\#2761](https://github.com/matrix-org/matrix-react-sdk/pull/2761) + * Always insert rooms into lists when they get lost + [\#2760](https://github.com/matrix-org/matrix-react-sdk/pull/2760) + * Fix the ctrl+k shortcut + [\#2759](https://github.com/matrix-org/matrix-react-sdk/pull/2759) + +Changes in [1.0.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.2) (2019-03-06) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.2-rc.4...v1.0.2) + + * Only show hosting link if group admin + [\#2755](https://github.com/matrix-org/matrix-react-sdk/pull/2755) + * Only show hosting link if domain == matrix.org + [\#2754](https://github.com/matrix-org/matrix-react-sdk/pull/2754) + +Changes in [1.0.2-rc.4](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.2-rc.4) (2019-03-05) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.2-rc.3...v1.0.2-rc.4) + + * Patch users not existing when opening settings + [\#2752](https://github.com/matrix-org/matrix-react-sdk/pull/2752) + * Fix media device selectors not updating + [\#2751](https://github.com/matrix-org/matrix-react-sdk/pull/2751) + * Support linking to hosting providers + [\#2750](https://github.com/matrix-org/matrix-react-sdk/pull/2750) + +Changes in [1.0.2-rc.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.2-rc.3) (2019-03-01) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.2-rc.2...v1.0.2-rc.3) + + * Misc room list improvements & invite fix + [\#2722](https://github.com/matrix-org/matrix-react-sdk/pull/2722) + * Move complex part of room sorting to a dedicated function + [\#2716](https://github.com/matrix-org/matrix-react-sdk/pull/2716) + +Changes in [1.0.2-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.2-rc.2) (2019-03-01) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.2-rc.1...v1.0.2-rc.2) + + * Use webpack alias to resolve the languages file + [\#2730](https://github.com/matrix-org/matrix-react-sdk/pull/2730) + +Changes in [1.0.2-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.2-rc.1) (2019-02-28) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.1...v1.0.2-rc.1) + + * Remove unnecessary ref from Stickerpicker + [\#2718](https://github.com/matrix-org/matrix-react-sdk/pull/2718) + * Update from Weblate + [\#2720](https://github.com/matrix-org/matrix-react-sdk/pull/2720) + * Update from Weblate. + [\#2715](https://github.com/matrix-org/matrix-react-sdk/pull/2715) + * Show nearest lower badge, e.g show Mod for 51... etc + [\#2709](https://github.com/matrix-org/matrix-react-sdk/pull/2709) + * Send Field label pointer events to input + [\#2714](https://github.com/matrix-org/matrix-react-sdk/pull/2714) + * move canSendMessages into state so that it will re-render the composer + [\#2710](https://github.com/matrix-org/matrix-react-sdk/pull/2710) + * Use a global WatchManager for settings + [\#2705](https://github.com/matrix-org/matrix-react-sdk/pull/2705) + * Move non-feather icons out of feather dir + [\#2706](https://github.com/matrix-org/matrix-react-sdk/pull/2706) + * Fix backup button in logout dialog + [\#2713](https://github.com/matrix-org/matrix-react-sdk/pull/2713) + * Keep registration spinner inside the auth modal + [\#2712](https://github.com/matrix-org/matrix-react-sdk/pull/2712) + * Try to clarify that "Show read receipts" is just for visibility + [\#2707](https://github.com/matrix-org/matrix-react-sdk/pull/2707) + * Make sure direct chat invites are treated as invites + [\#2711](https://github.com/matrix-org/matrix-react-sdk/pull/2711) + * Report validity state of all registration fields on any change + [\#2672](https://github.com/matrix-org/matrix-react-sdk/pull/2672) + * Export the defaults for SdkConfig + [\#2687](https://github.com/matrix-org/matrix-react-sdk/pull/2687) + * Change the room list algo to eagerly delete and carefully insert + [\#2701](https://github.com/matrix-org/matrix-react-sdk/pull/2701) + * Improve permalink performance + [\#2671](https://github.com/matrix-org/matrix-react-sdk/pull/2671) + * Normalise room list font weight, bold unreads + [\#2702](https://github.com/matrix-org/matrix-react-sdk/pull/2702) + * Settings button in Room Context Menu + [\#2692](https://github.com/matrix-org/matrix-react-sdk/pull/2692) + * add roomnick SlashCommand + [\#2689](https://github.com/matrix-org/matrix-react-sdk/pull/2689) + * replace text Inputs in Devtools with Field bcuz prettier + [\#2697](https://github.com/matrix-org/matrix-react-sdk/pull/2697) + * Maintenance: Clean up some stuff around settings + [\#2685](https://github.com/matrix-org/matrix-react-sdk/pull/2685) + * Support the old room sorting algorithm and SettingsStore watchers + [\#2686](https://github.com/matrix-org/matrix-react-sdk/pull/2686) + * pin highlight.js as they seem to have broken something in 9.15.2 + [\#2703](https://github.com/matrix-org/matrix-react-sdk/pull/2703) + * Fix NPE relating to toggling notifications + [\#2699](https://github.com/matrix-org/matrix-react-sdk/pull/2699) + * Only set e2e info callback if the event is encrypted + [\#2684](https://github.com/matrix-org/matrix-react-sdk/pull/2684) + * Revert "Add some logging for riot-web#7838" + [\#2700](https://github.com/matrix-org/matrix-react-sdk/pull/2700) + * Add abilities to clear Filter room members input + [\#2698](https://github.com/matrix-org/matrix-react-sdk/pull/2698) + * make ViewSource less awkward + [\#2696](https://github.com/matrix-org/matrix-react-sdk/pull/2696) + * Fix share community for guests + [\#2695](https://github.com/matrix-org/matrix-react-sdk/pull/2695) + * Fix z ordering of the overflow tile + [\#2694](https://github.com/matrix-org/matrix-react-sdk/pull/2694) + * Toggle Search using Room Header button + [\#2693](https://github.com/matrix-org/matrix-react-sdk/pull/2693) + * view user on click typing tile + [\#2691](https://github.com/matrix-org/matrix-react-sdk/pull/2691) + * Change Share Message to Share Permalink if !m.room.message||redacted + [\#2690](https://github.com/matrix-org/matrix-react-sdk/pull/2690) + * Allow configuration of whether closing window closes or minimizes to tray + [\#2688](https://github.com/matrix-org/matrix-react-sdk/pull/2688) + * Remove duplicated conversion from `enableRichText` + [\#2682](https://github.com/matrix-org/matrix-react-sdk/pull/2682) + * Use correct initial phase for server type + [\#2683](https://github.com/matrix-org/matrix-react-sdk/pull/2683) + * Fix AliasSettings and RelatedGroups UX + [\#2679](https://github.com/matrix-org/matrix-react-sdk/pull/2679) + * Add `onClick` to Markdown buttons + [\#2677](https://github.com/matrix-org/matrix-react-sdk/pull/2677) + * Fix toggle for email notifications + [\#2678](https://github.com/matrix-org/matrix-react-sdk/pull/2678) + * Fix categorization of favourites and new rooms + [\#2681](https://github.com/matrix-org/matrix-react-sdk/pull/2681) + * Skip server details on registration with a default HS + [\#2675](https://github.com/matrix-org/matrix-react-sdk/pull/2675) + * Clarify what the username error refers to + [\#2673](https://github.com/matrix-org/matrix-react-sdk/pull/2673) + * RoomDirectory Dropdown should use roomDirectory.servers + [\#2680](https://github.com/matrix-org/matrix-react-sdk/pull/2680) + * Fix favourites losing rooms and sorting weirdly + [\#2674](https://github.com/matrix-org/matrix-react-sdk/pull/2674) + * "Breadcrumb" room sorting algorithm + [\#2634](https://github.com/matrix-org/matrix-react-sdk/pull/2634) + * Bring back user view + [\#2666](https://github.com/matrix-org/matrix-react-sdk/pull/2666) + * Restores support for `default_server_name` which discovers URLs via `.well- + known` + [\#2669](https://github.com/matrix-org/matrix-react-sdk/pull/2669) + * Remove unreferenced images + [\#2670](https://github.com/matrix-org/matrix-react-sdk/pull/2670) + * Only show the first line of each commit in changelog dialog + [\#2667](https://github.com/matrix-org/matrix-react-sdk/pull/2667) + * Cache-bust languages.json with file-loader + [\#2658](https://github.com/matrix-org/matrix-react-sdk/pull/2658) + * Fix off by one error for username colors + [\#2664](https://github.com/matrix-org/matrix-react-sdk/pull/2664) + * Set event-sending-color in dark theme + [\#2665](https://github.com/matrix-org/matrix-react-sdk/pull/2665) + * Allow captchas on Riot desktop builds + [\#2663](https://github.com/matrix-org/matrix-react-sdk/pull/2663) + * Show link to login even during UI auth + [\#2661](https://github.com/matrix-org/matrix-react-sdk/pull/2661) + * Dark theme follow ups round 2 + [\#2659](https://github.com/matrix-org/matrix-react-sdk/pull/2659) + * Rename default theme to light theme + [\#2662](https://github.com/matrix-org/matrix-react-sdk/pull/2662) + * make mx_SenderProfile inline-block, reduces clickable area around usernames + [\#2653](https://github.com/matrix-org/matrix-react-sdk/pull/2653) + * Add a bit of safety around reading events for room settings + [\#2648](https://github.com/matrix-org/matrix-react-sdk/pull/2648) + * Update dark theme bg-color to show hover effect on messages + [\#2654](https://github.com/matrix-org/matrix-react-sdk/pull/2654) + * Make pre use the same text color as code + [\#2655](https://github.com/matrix-org/matrix-react-sdk/pull/2655) + * Bring back the lowered opacity for offline/unavailable avatars. + [\#2649](https://github.com/matrix-org/matrix-react-sdk/pull/2649) + * Set h3-color in dark theme + [\#2656](https://github.com/matrix-org/matrix-react-sdk/pull/2656) + * Fix typo "Scisors" -> "Scissors" + [\#2652](https://github.com/matrix-org/matrix-react-sdk/pull/2652) + * highlight e2e icon on event when hovering whole event + [\#2647](https://github.com/matrix-org/matrix-react-sdk/pull/2647) + * Skinnified more icons + [\#2633](https://github.com/matrix-org/matrix-react-sdk/pull/2633) + +Changes in [1.0.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.1) (2019-02-15) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.0...v1.0.1) + + * Fix community invites crashing the app + [\#2650](https://github.com/matrix-org/matrix-react-sdk/pull/2650) + +Changes in [1.0.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.0) (2019-02-14) +=================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.0-rc.2...v1.0.0) + + * Update from Weblate. + [\#2646](https://github.com/matrix-org/matrix-react-sdk/pull/2646) + * Remove 'welcome' from top-left menu + [\#2641](https://github.com/matrix-org/matrix-react-sdk/pull/2641) + * Turn on pin unread rooms for everyone + [\#2645](https://github.com/matrix-org/matrix-react-sdk/pull/2645) + * Update help buoy text and issue links + [\#2640](https://github.com/matrix-org/matrix-react-sdk/pull/2640) + * Fix icons being cut off in settings + [\#2644](https://github.com/matrix-org/matrix-react-sdk/pull/2644) + * Add credit for cover photo usage + [\#2643](https://github.com/matrix-org/matrix-react-sdk/pull/2643) + * make e2e icons on message transparent + [\#2642](https://github.com/matrix-org/matrix-react-sdk/pull/2642) + * fix close button being half off screen + [\#2639](https://github.com/matrix-org/matrix-react-sdk/pull/2639) + * Fix excessive timeline whitespace + [\#2638](https://github.com/matrix-org/matrix-react-sdk/pull/2638) + * Remove the white screen of welcome + [\#2637](https://github.com/matrix-org/matrix-react-sdk/pull/2637) + * always rerender room tiles + [\#2636](https://github.com/matrix-org/matrix-react-sdk/pull/2636) + +Changes in [1.0.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.0-rc.2) (2019-02-14) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.0.0-rc.1...v1.0.0-rc.2) + + * Update from Weblate. + [\#2635](https://github.com/matrix-org/matrix-react-sdk/pull/2635) + * use throttle as its more responsive + [\#2632](https://github.com/matrix-org/matrix-react-sdk/pull/2632) + * update range when items size changes + [\#2631](https://github.com/matrix-org/matrix-react-sdk/pull/2631) + * Fix registration after clicking email link + [\#2630](https://github.com/matrix-org/matrix-react-sdk/pull/2630) + * Re-check key backup status when settings opened + [\#2626](https://github.com/matrix-org/matrix-react-sdk/pull/2626) + * Improve room list rendering performance + [\#2629](https://github.com/matrix-org/matrix-react-sdk/pull/2629) + * Adjust top left menu items + [\#2628](https://github.com/matrix-org/matrix-react-sdk/pull/2628) + * Normalised icon strokes to 1px + [\#2627](https://github.com/matrix-org/matrix-react-sdk/pull/2627) + * Security: Force TURN setting was inverted + [\#2623](https://github.com/matrix-org/matrix-react-sdk/pull/2623) + * Add redesigned dark theme + [\#2619](https://github.com/matrix-org/matrix-react-sdk/pull/2619) + * Fix mx_RoomTile_name weighting + [\#2610](https://github.com/matrix-org/matrix-react-sdk/pull/2610) + * Add divider between tabs and regular buttons in room header + [\#2621](https://github.com/matrix-org/matrix-react-sdk/pull/2621) + * Update from Weblate. + [\#2622](https://github.com/matrix-org/matrix-react-sdk/pull/2622) + * Change taking a community off the left-left panel less scary + [\#2609](https://github.com/matrix-org/matrix-react-sdk/pull/2609) + * Fixes and styling related to e2e icons and dialogs + [\#2620](https://github.com/matrix-org/matrix-react-sdk/pull/2620) + * Fix: stickers layout + [\#2618](https://github.com/matrix-org/matrix-react-sdk/pull/2618) + * Fix: dont assume settings label only has one line + [\#2616](https://github.com/matrix-org/matrix-react-sdk/pull/2616) + * Labs feature: recent room breadcrumbs + [\#2615](https://github.com/matrix-org/matrix-react-sdk/pull/2615) + * Fix: roomlist reordering lags + [\#2612](https://github.com/matrix-org/matrix-react-sdk/pull/2612) + * Change text in e2e UX to new copy + [\#2617](https://github.com/matrix-org/matrix-react-sdk/pull/2617) + * Add display name / avatar to incoming sas dialog + [\#2613](https://github.com/matrix-org/matrix-react-sdk/pull/2613) + * Restore backup on new recovery method dialog + [\#2614](https://github.com/matrix-org/matrix-react-sdk/pull/2614) + * Welcome page cleanup + [\#2611](https://github.com/matrix-org/matrix-react-sdk/pull/2611) + * Scale up settings UI to be easier to read + [\#2604](https://github.com/matrix-org/matrix-react-sdk/pull/2604) + * !important shouldn't have a space + [\#2608](https://github.com/matrix-org/matrix-react-sdk/pull/2608) + * Add legacy verification button on wait + [\#2607](https://github.com/matrix-org/matrix-react-sdk/pull/2607) + * Update from Weblate. + [\#2606](https://github.com/matrix-org/matrix-react-sdk/pull/2606) + +Changes in [1.0.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.0.0-rc.1) (2019-02-08) +============================================================================================================= +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.8...v1.0.0-rc.1) + + * Call isGuest correctly + [\#2603](https://github.com/matrix-org/matrix-react-sdk/pull/2603) + * Update from Weblate. + [\#2602](https://github.com/matrix-org/matrix-react-sdk/pull/2602) + * Prompt to restore backup rather than verify + [\#2594](https://github.com/matrix-org/matrix-react-sdk/pull/2594) + * Remove key backup & sas from labs + [\#2599](https://github.com/matrix-org/matrix-react-sdk/pull/2599) + * Update avatar colors + [\#2600](https://github.com/matrix-org/matrix-react-sdk/pull/2600) + * Fix: typeerror when creating DM + [\#2601](https://github.com/matrix-org/matrix-react-sdk/pull/2601) + * Render disabled mxField textareas as disabled + [\#2591](https://github.com/matrix-org/matrix-react-sdk/pull/2591) + * SDK support for welcome page + [\#2597](https://github.com/matrix-org/matrix-react-sdk/pull/2597) + * Change SAS to decimal / emoji + [\#2596](https://github.com/matrix-org/matrix-react-sdk/pull/2596) + * Render join rules and guest access changes in the timeline + [\#2592](https://github.com/matrix-org/matrix-react-sdk/pull/2592) + * Ensure toggle switches listen to property changes + [\#2590](https://github.com/matrix-org/matrix-react-sdk/pull/2590) + * Local echo on room access settings + [\#2593](https://github.com/matrix-org/matrix-react-sdk/pull/2593) + * guard custom tags with feature flag + [\#2589](https://github.com/matrix-org/matrix-react-sdk/pull/2589) + * remove ll feature flag, it's time! + [\#2588](https://github.com/matrix-org/matrix-react-sdk/pull/2588) + * Trust on decrypt + [\#2583](https://github.com/matrix-org/matrix-react-sdk/pull/2583) + * Remove click-to-verify from SAS + [\#2586](https://github.com/matrix-org/matrix-react-sdk/pull/2586) + * Fix: make sure custom tag scroller doesn't overflow parent + [\#2587](https://github.com/matrix-org/matrix-react-sdk/pull/2587) + * Fix: throttle custom tags updating in LLP + [\#2585](https://github.com/matrix-org/matrix-react-sdk/pull/2585) + * Fix firefox scrolling settings tabs differently + [\#2579](https://github.com/matrix-org/matrix-react-sdk/pull/2579) + * Actually change power levels when they are changed + [\#2580](https://github.com/matrix-org/matrix-react-sdk/pull/2580) + * Fix: logging in again breaks CustomRoomTagStore + [\#2584](https://github.com/matrix-org/matrix-react-sdk/pull/2584) + * Fix: click on notif badge + [\#2582](https://github.com/matrix-org/matrix-react-sdk/pull/2582) + * Extend slash command '/topic' to display the room topic + [\#2532](https://github.com/matrix-org/matrix-react-sdk/pull/2532) + * Fix: community badges + [\#2581](https://github.com/matrix-org/matrix-react-sdk/pull/2581) + * Bring back custom tags, also badges on communities + [\#2575](https://github.com/matrix-org/matrix-react-sdk/pull/2575) + * Style reset password to match design + [\#2578](https://github.com/matrix-org/matrix-react-sdk/pull/2578) + * Key Backup: Don't fail if no keys + [\#2577](https://github.com/matrix-org/matrix-react-sdk/pull/2577) + * Remove old user and room settings + [\#2554](https://github.com/matrix-org/matrix-react-sdk/pull/2554) + * increase debouncing of filtering because its quite laggy atm + [\#2576](https://github.com/matrix-org/matrix-react-sdk/pull/2576) + * Tweak field padding to avoid overlapping with selected text + [\#2573](https://github.com/matrix-org/matrix-react-sdk/pull/2573) + * Adapt login flow for the v2 design + [\#2574](https://github.com/matrix-org/matrix-react-sdk/pull/2574) + * Remove the arrow-paren lint rule + [\#2572](https://github.com/matrix-org/matrix-react-sdk/pull/2572) + * Ensure we show registration form when custom URLs are disabled + [\#2571](https://github.com/matrix-org/matrix-react-sdk/pull/2571) + * Fix: search term disappears when collapsing and expanding left panel + [\#2568](https://github.com/matrix-org/matrix-react-sdk/pull/2568) + * Fix: 'jump to bottom' creates big amounts of whitespace at the bottom + [\#2567](https://github.com/matrix-org/matrix-react-sdk/pull/2567) + * Fix: being able to size sections in leftpanel larger than their content + while filtering + [\#2566](https://github.com/matrix-org/matrix-react-sdk/pull/2566) + * Redesign: widget makeover + [\#2565](https://github.com/matrix-org/matrix-react-sdk/pull/2565) + * Restore dropdown chevron to right + [\#2564](https://github.com/matrix-org/matrix-react-sdk/pull/2564) + * Remove warning about encryption being beta + [\#2563](https://github.com/matrix-org/matrix-react-sdk/pull/2563) + * Add e2e icon to room header/composer/member info, more ... + [\#2557](https://github.com/matrix-org/matrix-react-sdk/pull/2557) + * Remove guest warning bar + [\#2562](https://github.com/matrix-org/matrix-react-sdk/pull/2562) + * Style tweaks to support auth background + [\#2561](https://github.com/matrix-org/matrix-react-sdk/pull/2561) + * Set a minimum width on the settings tab content + [\#2560](https://github.com/matrix-org/matrix-react-sdk/pull/2560) + * Fix exception while saving room settings + [\#2555](https://github.com/matrix-org/matrix-react-sdk/pull/2555) + * Disable old settings, making tabbed settings the default + [\#2559](https://github.com/matrix-org/matrix-react-sdk/pull/2559) + * fix UnknownDeviceDialog layout + [\#2558](https://github.com/matrix-org/matrix-react-sdk/pull/2558) + * Misc fixes to settings + [\#2553](https://github.com/matrix-org/matrix-react-sdk/pull/2553) + * Add error message when registration is disabled + [\#2548](https://github.com/matrix-org/matrix-react-sdk/pull/2548) + * Hide registration fields that aren't used by any flow + [\#2551](https://github.com/matrix-org/matrix-react-sdk/pull/2551) + * Ensure correct server URLs with .well-known and server type + [\#2547](https://github.com/matrix-org/matrix-react-sdk/pull/2547) + * Spell homeserver correctly + [\#2552](https://github.com/matrix-org/matrix-react-sdk/pull/2552) + * Auto-focus username on registration + [\#2546](https://github.com/matrix-org/matrix-react-sdk/pull/2546) + * Fixed settings dialog header; Adjust padding on dialog + [\#2549](https://github.com/matrix-org/matrix-react-sdk/pull/2549) + * Fix empty lightbox when there is no avatarUrl + [\#2314](https://github.com/matrix-org/matrix-react-sdk/pull/2314) + * make overflow gradients much smaller and turn bottom into drop shadow + [\#2544](https://github.com/matrix-org/matrix-react-sdk/pull/2544) + * Make auth validation less annoying + [\#2539](https://github.com/matrix-org/matrix-react-sdk/pull/2539) + * layout composer independent of avatar being present + [\#2545](https://github.com/matrix-org/matrix-react-sdk/pull/2545) + * Matthew/cyrillic + [\#2543](https://github.com/matrix-org/matrix-react-sdk/pull/2543) + * Allow expanding the left panel manually when in narrow mode + [\#2541](https://github.com/matrix-org/matrix-react-sdk/pull/2541) + * Redesign: community page cleanup + [\#2538](https://github.com/matrix-org/matrix-react-sdk/pull/2538) + * Redesign: Disable ILAG + [\#2536](https://github.com/matrix-org/matrix-react-sdk/pull/2536) + * Use custom appearance and arrow for field selects + [\#2540](https://github.com/matrix-org/matrix-react-sdk/pull/2540) + * Fix typo + [\#2537](https://github.com/matrix-org/matrix-react-sdk/pull/2537) + * Merge redesign into develop + [\#2535](https://github.com/matrix-org/matrix-react-sdk/pull/2535) + * disable e2e tests everywhere as redesign breaks them for now + [\#2534](https://github.com/matrix-org/matrix-react-sdk/pull/2534) + * avoid horizontal scrollbar in composer when placeholder doesn't fit + [\#2533](https://github.com/matrix-org/matrix-react-sdk/pull/2533) + * fix dropdown style when input is shown + [\#2531](https://github.com/matrix-org/matrix-react-sdk/pull/2531) + * Redesign: tiny fix: stretch device label in member info if content doesn't + fill it + [\#2530](https://github.com/matrix-org/matrix-react-sdk/pull/2530) + * Style registration flow + [\#2527](https://github.com/matrix-org/matrix-react-sdk/pull/2527) + * Redesign: small member info panel makeover + [\#2522](https://github.com/matrix-org/matrix-react-sdk/pull/2522) + * Render the home page when viewing the directory + [\#2529](https://github.com/matrix-org/matrix-react-sdk/pull/2529) + * Fix indentation on all new settings CSS + [\#2528](https://github.com/matrix-org/matrix-react-sdk/pull/2528) + * Round 1 of misc fixes for settings + [\#2526](https://github.com/matrix-org/matrix-react-sdk/pull/2526) + * Implement the Security & Privacy tab of new room settings + [\#2523](https://github.com/matrix-org/matrix-react-sdk/pull/2523) + * Implement the Advanced tab of new room settings + [\#2525](https://github.com/matrix-org/matrix-react-sdk/pull/2525) + * Implement the Roles & Permissions tab of new room settings + [\#2524](https://github.com/matrix-org/matrix-react-sdk/pull/2524) + * Redesign: room directory makeover + [\#2519](https://github.com/matrix-org/matrix-react-sdk/pull/2519) + * Iterate upon the room upgrade warning bar + [\#2518](https://github.com/matrix-org/matrix-react-sdk/pull/2518) + * redesign: small fixes + [\#2520](https://github.com/matrix-org/matrix-react-sdk/pull/2520) + * Implement the "general" tab of new room settings + [\#2516](https://github.com/matrix-org/matrix-react-sdk/pull/2516) + * Tweak auth overflow on Windows and Linux + [\#2521](https://github.com/matrix-org/matrix-react-sdk/pull/2521) + * Redesign: switch layout when filtering room sublists + [\#2515](https://github.com/matrix-org/matrix-react-sdk/pull/2515) + * Make native scrollbars prettier + [\#2470](https://github.com/matrix-org/matrix-react-sdk/pull/2470) + * Add server type selector and style login flow + [\#2517](https://github.com/matrix-org/matrix-react-sdk/pull/2517) + * Implement flair tab in user settings + [\#2512](https://github.com/matrix-org/matrix-react-sdk/pull/2512) + * Override UA/OS styles for disabled Field selects + [\#2502](https://github.com/matrix-org/matrix-react-sdk/pull/2502) + * Be more positive with setting labels + [\#2504](https://github.com/matrix-org/matrix-react-sdk/pull/2504) + * Redesign: new roomlist layout fixes + [\#2514](https://github.com/matrix-org/matrix-react-sdk/pull/2514) + * Redesign: new layout algorithm for room sublists. + [\#2507](https://github.com/matrix-org/matrix-react-sdk/pull/2507) + * Short-Authentication-String Verification + [\#2461](https://github.com/matrix-org/matrix-react-sdk/pull/2461) + * Fix unmount TypeError in `DeviceVerifyButtons` + [\#2513](https://github.com/matrix-org/matrix-react-sdk/pull/2513) + * Remove support for team servers + [\#2511](https://github.com/matrix-org/matrix-react-sdk/pull/2511) + * Initial structure for new room settings + [\#2510](https://github.com/matrix-org/matrix-react-sdk/pull/2510) + * Tweak wording on logout warning + [\#2509](https://github.com/matrix-org/matrix-react-sdk/pull/2509) + * Fix NPE in RoomRecoveryReminder + [\#2508](https://github.com/matrix-org/matrix-react-sdk/pull/2508) + * New text/caption for key backup by verifying device + [\#2506](https://github.com/matrix-org/matrix-react-sdk/pull/2506) + * Implement the "Security & Privacy" tab of new user settings + [\#2499](https://github.com/matrix-org/matrix-react-sdk/pull/2499) + * Add simple animations to toggle switches + [\#2505](https://github.com/matrix-org/matrix-react-sdk/pull/2505) + * Default a Field's placeholder to the label + [\#2503](https://github.com/matrix-org/matrix-react-sdk/pull/2503) + * Have the settings dialog be fixed in size + [\#2501](https://github.com/matrix-org/matrix-react-sdk/pull/2501) + * Implement the "Help & About" tab of new user settings + [\#2500](https://github.com/matrix-org/matrix-react-sdk/pull/2500) + * Implement the "Voice & Video" tab of new user settings + [\#2498](https://github.com/matrix-org/matrix-react-sdk/pull/2498) + * Add widget screenshots to the Labs section + [\#2497](https://github.com/matrix-org/matrix-react-sdk/pull/2497) + * Implement the "Preferences" tab on new user settings + [\#2495](https://github.com/matrix-org/matrix-react-sdk/pull/2495) + * Add target="_blank" to links that don't have it + [\#2496](https://github.com/matrix-org/matrix-react-sdk/pull/2496) + * Implement the "Notifications" tab of new user settings + [\#2494](https://github.com/matrix-org/matrix-react-sdk/pull/2494) + * Implement the "Labs" tab of new user settings + [\#2492](https://github.com/matrix-org/matrix-react-sdk/pull/2492) + * Implement the "General" tab of new user settings + [\#2491](https://github.com/matrix-org/matrix-react-sdk/pull/2491) + * Appease linter in auth related files + [\#2493](https://github.com/matrix-org/matrix-react-sdk/pull/2493) + * Update text and links in authentication flows + [\#2489](https://github.com/matrix-org/matrix-react-sdk/pull/2489) + * Move LanguageSelector to views + [\#2490](https://github.com/matrix-org/matrix-react-sdk/pull/2490) + * Restyle auth page language selector + [\#2488](https://github.com/matrix-org/matrix-react-sdk/pull/2488) + * Fix desktop captcha check + [\#2487](https://github.com/matrix-org/matrix-react-sdk/pull/2487) + * Basic structure for tabbed user settings + [\#2476](https://github.com/matrix-org/matrix-react-sdk/pull/2476) + * Token encouragement if zxcvbn gives no feedback + [\#2471](https://github.com/matrix-org/matrix-react-sdk/pull/2471) + * Fix: show rooms and people section when empty while filtering + [\#2481](https://github.com/matrix-org/matrix-react-sdk/pull/2481) + * Fix AuthFooter CSS rules conflicting with anchors all over the app + [\#2486](https://github.com/matrix-org/matrix-react-sdk/pull/2486) + * Support selects on Field + [\#2484](https://github.com/matrix-org/matrix-react-sdk/pull/2484) + * Fix integrations server error popup being hidden behind right panel + [\#2482](https://github.com/matrix-org/matrix-react-sdk/pull/2482) + * Fix: apparently room can be null here + [\#2480](https://github.com/matrix-org/matrix-react-sdk/pull/2480) + * Redesign: pull jump to bottom button out of room status bar + [\#2478](https://github.com/matrix-org/matrix-react-sdk/pull/2478) + * Redesign: set default size of 350px for left panel + [\#2479](https://github.com/matrix-org/matrix-react-sdk/pull/2479) + * Avoid "jumpiness" with inline typing indicator + [\#2456](https://github.com/matrix-org/matrix-react-sdk/pull/2456) + * De-lint CompatabilityPage & LoggedInView + [\#2472](https://github.com/matrix-org/matrix-react-sdk/pull/2472) + * Remove Status theme-specific hacks + [\#2473](https://github.com/matrix-org/matrix-react-sdk/pull/2473) + * Error if no sessions decrypted + [\#2469](https://github.com/matrix-org/matrix-react-sdk/pull/2469) + * Fix settings direct chat + [\#2466](https://github.com/matrix-org/matrix-react-sdk/pull/2466) + * Show verify button when we have a device to verify + [\#2464](https://github.com/matrix-org/matrix-react-sdk/pull/2464) + * Redesign: Add a form field component + [\#2463](https://github.com/matrix-org/matrix-react-sdk/pull/2463) + * Load fonts and images via source-relative URLs and requires + [\#2460](https://github.com/matrix-org/matrix-react-sdk/pull/2460) + * Say when backup is signed by unknown device + [\#2455](https://github.com/matrix-org/matrix-react-sdk/pull/2455) + * Add an /upgraderoom command to make upgrading easier for development + [\#2458](https://github.com/matrix-org/matrix-react-sdk/pull/2458) + * Merge develop->experimental + [\#2457](https://github.com/matrix-org/matrix-react-sdk/pull/2457) + * Fix: show hand cursor in topleft menu so its clear you can click it + [\#2454](https://github.com/matrix-org/matrix-react-sdk/pull/2454) + * Fix: search makeover missing icons + [\#2453](https://github.com/matrix-org/matrix-react-sdk/pull/2453) + * Redesign: search makeover + [\#2448](https://github.com/matrix-org/matrix-react-sdk/pull/2448) + * Revert "Tiled room UI" + [\#2451](https://github.com/matrix-org/matrix-react-sdk/pull/2451) + * Update from Weblate. + [\#2452](https://github.com/matrix-org/matrix-react-sdk/pull/2452) + * Improve room sublist resizing + [\#2440](https://github.com/matrix-org/matrix-react-sdk/pull/2440) + * Different dialog for new trusted backup + [\#2435](https://github.com/matrix-org/matrix-react-sdk/pull/2435) + * De-lint a few more files + [\#2436](https://github.com/matrix-org/matrix-react-sdk/pull/2436) + * Recalculate the visible rooms when rooms are upgraded + [\#2433](https://github.com/matrix-org/matrix-react-sdk/pull/2433) + * Navigate to the upgraded room's create event where possible + [\#2432](https://github.com/matrix-org/matrix-react-sdk/pull/2432) + * Don't show rooms with tombstones in the address picker + [\#2429](https://github.com/matrix-org/matrix-react-sdk/pull/2429) + * Add separate dialog for recovery method removed + [\#2427](https://github.com/matrix-org/matrix-react-sdk/pull/2427) + * Set which servers to try and join upgraded rooms through + [\#2428](https://github.com/matrix-org/matrix-react-sdk/pull/2428) + * Render a tile for tombstone events + [\#2430](https://github.com/matrix-org/matrix-react-sdk/pull/2430) + * Regenerate en_EN.json to sort entries + [\#2431](https://github.com/matrix-org/matrix-react-sdk/pull/2431) + * Key backup: Debounce passphrase feedback + [\#2426](https://github.com/matrix-org/matrix-react-sdk/pull/2426) + * Set backup niggles: 2 + [\#2425](https://github.com/matrix-org/matrix-react-sdk/pull/2425) + * Fix lint errors in MessageComposerInput + [\#2423](https://github.com/matrix-org/matrix-react-sdk/pull/2423) + * Set backup niggles: 1 + [\#2424](https://github.com/matrix-org/matrix-react-sdk/pull/2424) + * PoC: Add simple state counters to room heading + [\#2388](https://github.com/matrix-org/matrix-react-sdk/pull/2388) + * Fix a few things with cancelling recovery reminder + [\#2420](https://github.com/matrix-org/matrix-react-sdk/pull/2420) + * Add spaces back to async arrow functions + [\#2422](https://github.com/matrix-org/matrix-react-sdk/pull/2422) + * fix grid growing wider than viewport on chrome + [\#2421](https://github.com/matrix-org/matrix-react-sdk/pull/2421) + * Tiled room UI + [\#2348](https://github.com/matrix-org/matrix-react-sdk/pull/2348) + * Fix path to New Recovery Method icon + [\#2417](https://github.com/matrix-org/matrix-react-sdk/pull/2417) + * run unit tests on riot-web like before + [\#2419](https://github.com/matrix-org/matrix-react-sdk/pull/2419) + * Refactor travis-ci to use parallel jobs + [\#2414](https://github.com/matrix-org/matrix-react-sdk/pull/2414) + * Fix black-on-black GIF icon for stickers + [\#2408](https://github.com/matrix-org/matrix-react-sdk/pull/2408) + * Don't reset cached room list values when they are falsey + [\#2413](https://github.com/matrix-org/matrix-react-sdk/pull/2413) + * Make logout warning nag about key backups + [\#2407](https://github.com/matrix-org/matrix-react-sdk/pull/2407) + * Clarify readme instructions for developers + [\#2404](https://github.com/matrix-org/matrix-react-sdk/pull/2404) + * Add slash command for changing room name + [\#2401](https://github.com/matrix-org/matrix-react-sdk/pull/2401) + * Flatten and simplify the memberlist sorting algorithm + [\#2381](https://github.com/matrix-org/matrix-react-sdk/pull/2381) + * Tiny fixes for custom status messages on experimental + [\#2403](https://github.com/matrix-org/matrix-react-sdk/pull/2403) + * Part 3 of 3: Apply today's changes to experimental again + [\#2400](https://github.com/matrix-org/matrix-react-sdk/pull/2400) + * Part 2 of 3: Merge develop->experimental minus #2336 + [\#2399](https://github.com/matrix-org/matrix-react-sdk/pull/2399) + * Part 1 of 3: Back out bad merge for develop->experimental + [\#2398](https://github.com/matrix-org/matrix-react-sdk/pull/2398) + * Fix browser navigation not working between /home, /login, /register, etc + [\#2383](https://github.com/matrix-org/matrix-react-sdk/pull/2383) + * Don't re-sort the room list if the user is hovering over it + [\#2396](https://github.com/matrix-org/matrix-react-sdk/pull/2396) + * Merge develop into experimental + [\#2395](https://github.com/matrix-org/matrix-react-sdk/pull/2395) + * Added colour var to all themes + [\#2379](https://github.com/matrix-org/matrix-react-sdk/pull/2379) + * Colour, contrast & legibility improvements + [\#2378](https://github.com/matrix-org/matrix-react-sdk/pull/2378) + * Redesign: add feedback dialog & button in tag panel + [\#2376](https://github.com/matrix-org/matrix-react-sdk/pull/2376) + * Redesign: add badge with dot to rm button, to see it catches your eye better + [\#2371](https://github.com/matrix-org/matrix-react-sdk/pull/2371) + * Fix misaligned (+) icon + [\#2374](https://github.com/matrix-org/matrix-react-sdk/pull/2374) + * Avoid 'transparent black' gradients in left panel + [\#2373](https://github.com/matrix-org/matrix-react-sdk/pull/2373) + * Normalised icons + [\#2370](https://github.com/matrix-org/matrix-react-sdk/pull/2370) + * Redesign: give right panel default width + [\#2369](https://github.com/matrix-org/matrix-react-sdk/pull/2369) + * Redesign: Fix login field looking inline + [\#2368](https://github.com/matrix-org/matrix-react-sdk/pull/2368) + * Redesign: select search query on focus + [\#2367](https://github.com/matrix-org/matrix-react-sdk/pull/2367) + * Redesign: fix remaining right panel collapse issues. + [\#2366](https://github.com/matrix-org/matrix-react-sdk/pull/2366) + * Redesign: left panel fixes + [\#2364](https://github.com/matrix-org/matrix-react-sdk/pull/2364) + * Redesign: allow to hide the right panel when clicking already active button + & persist + [\#2361](https://github.com/matrix-org/matrix-react-sdk/pull/2361) + * Redesign: make room tiles less high so more rooms fit on the screen + [\#2359](https://github.com/matrix-org/matrix-react-sdk/pull/2359) + * Redesign: ignore any unknown tags + [\#2358](https://github.com/matrix-org/matrix-react-sdk/pull/2358) + * Redesign: disable setting theme completely + [\#2357](https://github.com/matrix-org/matrix-react-sdk/pull/2357) + * Force use of dharma theme + [\#2355](https://github.com/matrix-org/matrix-react-sdk/pull/2355) + * Redesign: some small fixes + [\#2354](https://github.com/matrix-org/matrix-react-sdk/pull/2354) + * Redesign: restyle jump to first unread message & rework read marker logic + (rebased) + [\#2345](https://github.com/matrix-org/matrix-react-sdk/pull/2345) + * Redesign: fix add room button alignment when collapsed + [\#2343](https://github.com/matrix-org/matrix-react-sdk/pull/2343) + * Redesign: confirm sign out from top left menu + [\#2342](https://github.com/matrix-org/matrix-react-sdk/pull/2342) + * Redesign: fix room header avatar in edit mode + [\#2344](https://github.com/matrix-org/matrix-react-sdk/pull/2344) + * Redesign: make community UX usable + [\#2341](https://github.com/matrix-org/matrix-react-sdk/pull/2341) + * Redesign: resizer persistence + [\#2321](https://github.com/matrix-org/matrix-react-sdk/pull/2321) + * Redesign: improve room sub list sizing & persist sizes + [\#2297](https://github.com/matrix-org/matrix-react-sdk/pull/2297) + * Redesign: temp solution to make room settings usable + [\#2298](https://github.com/matrix-org/matrix-react-sdk/pull/2298) + * Redesign: typing notifications in timeline + [\#2276](https://github.com/matrix-org/matrix-react-sdk/pull/2276) + * Redesign: add scroll indicator gradients to top and bottom of room sub list + [\#2275](https://github.com/matrix-org/matrix-react-sdk/pull/2275) + * Redesign: move member query field to bottom of member list + [\#2270](https://github.com/matrix-org/matrix-react-sdk/pull/2270) + * Redesign: room list visual polish + [\#2269](https://github.com/matrix-org/matrix-react-sdk/pull/2269) + * Redesign: bring back & restyle room filter field + [\#2267](https://github.com/matrix-org/matrix-react-sdk/pull/2267) + * Redesign: increase interaction rectangle of resize handles + [\#2262](https://github.com/matrix-org/matrix-react-sdk/pull/2262) + * Redesign: move right panel below room/group header + [\#2260](https://github.com/matrix-org/matrix-react-sdk/pull/2260) + * Redesign: use native auto-hiding scrollbars in room sub lists + [\#2264](https://github.com/matrix-org/matrix-react-sdk/pull/2264) + * Redesign: basic makeover of member info panel + [\#2248](https://github.com/matrix-org/matrix-react-sdk/pull/2248) + * Redesign: memberlist basic makeover + [\#2245](https://github.com/matrix-org/matrix-react-sdk/pull/2245) + * Redesign: tweak room list font sizes + [\#2246](https://github.com/matrix-org/matrix-react-sdk/pull/2246) + * Redesign: Fix room lists sizing + [\#2234](https://github.com/matrix-org/matrix-react-sdk/pull/2234) + * Redesign: fix import path + [\#2243](https://github.com/matrix-org/matrix-react-sdk/pull/2243) + * Redesign: update (most) icons + [\#2241](https://github.com/matrix-org/matrix-react-sdk/pull/2241) + * Redesign: fix basic room header layout + [\#2240](https://github.com/matrix-org/matrix-react-sdk/pull/2240) + * Redesign: 1st go at top left menu & restyling context menus + [\#2239](https://github.com/matrix-org/matrix-react-sdk/pull/2239) + * Redesign: Initial timeline tweaks + [\#2238](https://github.com/matrix-org/matrix-react-sdk/pull/2238) + * Redesign: Align visuals of room list with design + [\#2233](https://github.com/matrix-org/matrix-react-sdk/pull/2233) + * Redesign: room section header tidbits + [\#2229](https://github.com/matrix-org/matrix-react-sdk/pull/2229) + * Redesign: Add (+) button in room section header to add rooms + [\#2228](https://github.com/matrix-org/matrix-react-sdk/pull/2228) + * Redesign: 1st go at resizing room sublists + [\#2226](https://github.com/matrix-org/matrix-react-sdk/pull/2226) + * Redesign: remove room list truncation and DND + [\#2224](https://github.com/matrix-org/matrix-react-sdk/pull/2224) + * Redesign: resizeable/collapsible sections + [\#2210](https://github.com/matrix-org/matrix-react-sdk/pull/2210) + Changes in [0.14.8](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.14.8) (2019-01-22) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.14.8-rc.1...v0.14.8) diff --git a/README.md b/README.md index c70f68902b..e944b04ff2 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ Developer Guide Platform Targets: * Chrome, Firefox and Safari. - * Edge should also work, but we're not testing it proactively. * 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 @@ -66,7 +65,7 @@ practices that anyone working with the SDK needs to be be aware of and uphold: component is a view or a structure, and then a broad functional grouping (e.g. 'rooms' here) - * After creating a new component you must run `npm run reskindex` to regenerate + * After creating a new component you must run `yarn reskindex` to regenerate the `component-index.js` for the SDK (used in future for skinning) * The view's CSS file MUST have the same name (e.g. view/rooms/MessageTile.css). @@ -131,26 +130,35 @@ for now. Development =========== -Ensure you have the latest stable Node JS runtime installed (v8.x is the best choice). Then check out -the code and pull in dependencies: +Ensure you have the latest LTS version of Node.js installed. + +Using `yarn` instead of `npm` is recommended. Please see the Yarn [install +guide](https://yarnpkg.com/docs/install/) if you do not have it already. + +`matrix-react-sdk` depends on `matrix-js-sdk`. To make use of changes in the +latter and to ensure tests run against the develop branch of `matrix-js-sdk`, +you should set up `matrix-js-sdk`: ```bash -git clone https://github.com/matrix-org/matrix-react-sdk.git +git clone https://github.com/matrix-org/matrix-js-sdk +cd matrix-js-sdk +git checkout develop +yarn link +yarn install +``` + +Then check out `matrix-react-sdk` and pull in dependencies: + +```bash +git clone https://github.com/matrix-org/matrix-react-sdk cd matrix-react-sdk git checkout develop -npm install +yarn link matrix-js-sdk +yarn install ``` -`matrix-react-sdk` depends on `matrix-js-sdk`. To make use of changes in the -latter and to ensure tests run against the develop branch of `matrix-js-sdk`, -you should run the following which will sync changes from the JS sdk here. - -```bash -npm link ../matrix-js-sdk -``` - -Command assumes a checked out and installed `matrix-js-sdk` folder in parent -folder. +See the [help for `yarn link`](https://yarnpkg.com/docs/cli/link) for more +details about this. Running tests ============= @@ -158,5 +166,5 @@ Running tests Ensure you've followed the above development instructions and then: ```bash -npm run test +yarn test ``` diff --git a/docs/settings.md b/docs/settings.md index cdba01e04a..1ba8981f84 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -1,11 +1,15 @@ # Settings Reference -This document serves as developer documentation for using "Granular Settings". Granular Settings allow users to specify different values for a setting at particular levels of interest. For example, a user may say that in a particular room they want URL previews off, but in all other rooms they want them enabled. The `SettingsStore` helps mask the complexity of dealing with the different levels and exposes easy to use getters and setters. +This document serves as developer documentation for using "Granular Settings". Granular Settings allow users to specify +different values for a setting at particular levels of interest. For example, a user may say that in a particular room +they want URL previews off, but in all other rooms they want them enabled. The `SettingsStore` helps mask the complexity +of dealing with the different levels and exposes easy to use getters and setters. ## Levels -Granular Settings rely on a series of known levels in order to use the correct value for the scenario. These levels, in order of prioirty, are: +Granular Settings rely on a series of known levels in order to use the correct value for the scenario. These levels, in +order of prioirty, are: * `device` - The current user's device * `room-device` - The current user's device, but only when in a specific room * `room-account` - The current user's account, but only when in a specific room @@ -14,12 +18,14 @@ Granular Settings rely on a series of known levels in order to use the correct v * `config` - Values are defined by `config.json` * `default` - The hardcoded default for the settings -Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure that room administrators cannot force account-only settings upon participants. +Individual settings may control which levels are appropriate for them as part of the defaults. This is often to ensure +that room administrators cannot force account-only settings upon participants. ## Settings -Settings are the different options a user may set or experience in the application. These are pre-defined in `src/settings/Settings.js` under the `SETTINGS` constant and have the following minimum requirements: +Settings are the different options a user may set or experience in the application. These are pre-defined in +`src/settings/Settings.js` under the `SETTINGS` constant and have the following minimum requirements: ``` // The ID is used to reference the setting throughout the application. This must be unique. "theSettingId": { @@ -47,13 +53,21 @@ Settings are the different options a user may set or experience in the applicati ### Getting values for a setting -After importing `SettingsStore`, simply make a call to `SettingsStore.getValue`. The `roomId` parameter should always be supplied where possible, even if the setting does not have a per-room level value. This is to ensure that the value returned is best represented in the room, particularly if the setting ever gets a per-room level in the future. +After importing `SettingsStore`, simply make a call to `SettingsStore.getValue`. The `roomId` parameter should always +be supplied where possible, even if the setting does not have a per-room level value. This is to ensure that the value +returned is best represented in the room, particularly if the setting ever gets a per-room level in the future. -In settings pages it is often desired to have the value at a particular level instead of getting the calculated value. Call `SettingsStore.getValueAt` to get the value of a setting at a particular level, and optionally make it explicitly at that level. By default `getValueAt` will traverse the tree starting at the provided level; making it explicit means it will not go beyond the provided level. When using `getValueAt`, please be sure to use `SettingLevel` to represent the target level. +In settings pages it is often desired to have the value at a particular level instead of getting the calculated value. +Call `SettingsStore.getValueAt` to get the value of a setting at a particular level, and optionally make it explicitly +at that level. By default `getValueAt` will traverse the tree starting at the provided level; making it explicit means +it will not go beyond the provided level. When using `getValueAt`, please be sure to use `SettingLevel` to represent the +target level. ### Setting values for a setting -Values are defined at particular levels and should be done in a safe manner. There are two checks to perform to ensure a clean save: is the level supported and can the user actually set the value. In most cases, neither should be an issue although there are circumstances where this changes. An example of a safe call is: +Values are defined at particular levels and should be done in a safe manner. There are two checks to perform to ensure a +clean save: is the level supported and can the user actually set the value. In most cases, neither should be an issue +although there are circumstances where this changes. An example of a safe call is: ```javascript const isSupported = SettingsStore.isLevelSupported(SettingLevel.ROOM); if (isSupported) { @@ -64,11 +78,13 @@ if (isSupported) { } ``` -These checks may also be performed in different areas of the application to avoid the verbose example above. For instance, the component which allows changing the setting may be hidden conditionally on the above conditions. +These checks may also be performed in different areas of the application to avoid the verbose example above. For +instance, the component which allows changing the setting may be hidden conditionally on the above conditions. ##### `SettingsFlag` component -Where possible, the `SettingsFlag` component should be used to set simple "flip-a-bit" (true/false) settings. The `SettingsFlag` also supports simple radio button options, such as the theme the user would like to use. +Where possible, the `SettingsFlag` component should be used to set simple "flip-a-bit" (true/false) settings. The +`SettingsFlag` also supports simple radio button options, such as the theme the user would like to use. ```html { @@ -131,24 +170,69 @@ SettingsStore.getValue(...); // this will return the value set in `setValue` abo ``` +## Watching for changes + +Most use cases do not need to set up a watcher because they are able to react to changes as they are made, or the +changes which are made are not significant enough for it to matter. Watchers are intended to be used in scenarios where +it is important to react to changes made by other logged in devices. Typically, this would be done within the component +itself, however the component should not be aware of the intricacies of setting inversion or remapping to particular +data structures. Instead, a generic watcher interface is provided on `SettingsStore` to watch (and subsequently unwatch) +for changes in a setting. + +An example of a watcher in action would be: + +```javascript +class MyComponent extends React.Component { + + settingWatcherRef = null; + + componentWillMount() { + const callback = (settingName, roomId, level, newValAtLevel, newVal) => { + this.setState({color: newVal}); + }; + this.settingWatcherRef = SettingsStore.watchSetting("roomColor", "!example:matrix.org", callback); + } + + componentWillUnmount() { + SettingsStore.unwatchSetting(this.settingWatcherRef); + } +} +``` + # Maintainers Reference -The granular settings system has a few complex parts to power it. This section is to document how the `SettingsStore` is supposed to work. +The granular settings system has a few complex parts to power it. This section is to document how the `SettingsStore` is +supposed to work. ### General information -The `SettingsStore` uses the hardcoded `LEVEL_ORDER` constant to ensure that it is using the correct override procedure. The array is checked from left to right, simulating the behaviour of overriding values from the higher levels. Each level should be defined in this array, including `default`. +The `SettingsStore` uses the hardcoded `LEVEL_ORDER` constant to ensure that it is using the correct override procedure. +The array is checked from left to right, simulating the behaviour of overriding values from the higher levels. Each +level should be defined in this array, including `default`. -Handlers (`src/settings/handlers/SettingsHandler.js`) represent a single level and are responsible for getting and setting values at that level. Handlers also provide additional information to the `SettingsStore` such as if the level is supported or if the current user may set values at the level. The `SettingsStore` will use the handler to enforce checks and manipulate settings. Handlers are also responsible for dealing with migration patterns or legacy settings for their level (for example, a setting being renamed or using a different key from other settings in the underlying store). Handlers are provided to the `SettingsStore` via the `LEVEL_HANDLERS` constant. `SettingsStore` will optimize lookups by only considering handlers that are supported on the platform. +Handlers (`src/settings/handlers/SettingsHandler.js`) represent a single level and are responsible for getting and +setting values at that level. Handlers also provide additional information to the `SettingsStore` such as if the level +is supported or if the current user may set values at the level. The `SettingsStore` will use the handler to enforce +checks and manipulate settings. Handlers are also responsible for dealing with migration patterns or legacy settings for +their level (for example, a setting being renamed or using a different key from other settings in the underlying store). +Handlers are provided to the `SettingsStore` via the `LEVEL_HANDLERS` constant. `SettingsStore` will optimize lookups by +only considering handlers that are supported on the platform. -Local echo is achieved through `src/settings/handlers/LocalEchoWrapper.js` which acts as a wrapper around a given handler. This is automatically applied to all defined `LEVEL_HANDLERS` and proxies the calls to the wrapped handler where possible. The echo is achieved by a simple object cache stored within the class itself. The cache is invalidated immediately upon the proxied save call succeeding or failing. +Local echo is achieved through `src/settings/handlers/LocalEchoWrapper.js` which acts as a wrapper around a given +handler. This is automatically applied to all defined `LEVEL_HANDLERS` and proxies the calls to the wrapped handler +where possible. The echo is achieved by a simple object cache stored within the class itself. The cache is invalidated +immediately upon the proxied save call succeeding or failing. -Controllers are notified of changes by the `SettingsStore`, and are given the opportunity to override values after the `SettingsStore` has deemed the value calculated. Controllers are invoked as the last possible step in the code. +Controllers are notified of changes by the `SettingsStore`, and are given the opportunity to override values after the +`SettingsStore` has deemed the value calculated. Controllers are invoked as the last possible step in the code. ### Features -Features automatically get considered as `disabled` if they are not listed in the `SdkConfig` or `enable_labs` is false/not set. Features are always checked against the configuration before going through the level order as they have the option of being forced-on or forced-off for the application. This is done by the `features` section and looks something like this: +Features automatically get considered as `disabled` if they are not listed in the `SdkConfig` or `enableLabs` is +false/not set. Features are always checked against the configuration before going through the level order as they have +the option of being forced-on or forced-off for the application. This is done by the `features` section and looks +something like this: ``` "features": { @@ -159,3 +243,21 @@ Features automatically get considered as `disabled` if they are not listed in th ``` If `enableLabs` is true in the configuration, the default for features becomes `"labs"`. + +### Watchers + +Watchers can appear complicated under the hood: there is a central `WatchManager` which handles the actual invocation +of callbacks, and callbacks are managed by the SettingsStore by redirecting the caller's callback to a dedicated +callback. This is done so that the caller can reuse the same function as their callback without worrying about whether +or not it'll unsubscribe all watchers. + +Setting changes are emitted into the default `WatchManager`, which calculates the new value for the setting. Ideally, +we'd also try and suppress updates which don't have a consequence on this value, however there's not an easy way to do +this. Instead, we just dispatch an update for all changes and leave it up to the consumer to deduplicate. + +In practice, handlers which rely on remote changes (account data, room events, etc) will always attach a listener to the +`MatrixClient`. They then watch for changes to events they care about and send off appropriate updates to the +generalized `WatchManager` - a class specifically designed to deduplicate the logic of managing watchers. The handlers +which are localized to the local client (device) generally just trigger the `WatchManager` when they manipulate the +setting themselves as there's nothing to really 'watch'. + diff --git a/jenkins.sh b/jenkins.sh index f4bb8da449..70bc12e42d 100755 --- a/jenkins.sh +++ b/jenkins.sh @@ -8,25 +8,32 @@ nvm use 10 set -x -# install the other dependencies -npm install - scripts/fetchdep.sh matrix-org matrix-js-sdk -rm -r node_modules/matrix-js-sdk || true -ln -s ../matrix-js-sdk node_modules/matrix-js-sdk -(cd matrix-js-sdk && npm install) + +pushd matrix-js-sdk +yarn link +yarn install +popd + +yarn link matrix-js-sdk + +# install the other dependencies +yarn install # run the mocha tests -npm run test -- --no-colors +yarn test --no-colors # run eslint -npm run lintall -- -f checkstyle -o eslint.xml || true +yarn lintall -f checkstyle -o eslint.xml || true # re-run the linter, excluding any files known to have errors or warnings. -npm run lintwithexclusions +yarn lintwithexclusions + +# lint styles +yarn stylelint # delete the old tarball, if it exists rm -f matrix-react-sdk-*.tgz # build our tarball -npm pack +yarn pack diff --git a/karma.conf.js b/karma.conf.js index 93063dbc04..e2728cdc09 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -8,7 +8,7 @@ var fs = require('fs'); * to build everything; however it's the easiest way to load our dependencies * from node_modules. * - * If you run karma in multi-run mode (with `npm run test-multi`), it will watch + * If you run karma in multi-run mode (with `yarn test-multi`), it will watch * the tests for changes, and webpack will rebuild using a cache. This is much quicker * than a clean rebuild. */ @@ -35,7 +35,7 @@ function fileExists(name) { } } -// try find the gemini-scrollbar css in an npm-version-agnostic way +// try find the gemini-scrollbar css in an version-agnostic way var gsCss = 'node_modules/gemini-scrollbar/gemini-scrollbar.css'; if (!fileExists(gsCss)) { gsCss = 'node_modules/react-gemini-scrollbar/'+gsCss; @@ -94,7 +94,7 @@ module.exports = function (config) { // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter - reporters: ['logcapture', 'spec', 'junit', 'summary'], + reporters: ['logcapture', 'spec', 'summary'], specReporter: { suppressErrorSummary: false, // do print error summary @@ -135,9 +135,10 @@ module.exports = function (config) { ], customLaunchers: { - 'ChromeHeadless': { + 'VectorChromeHeadless': { base: 'Chrome', flags: [ + '--no-sandbox', // See https://chromium.googlesource.com/chromium/src/+/lkgr/headless/README.md '--headless', '--disable-gpu', @@ -155,10 +156,6 @@ module.exports = function (config) { // how many browser should be started simultaneous concurrency: Infinity, - junitReporter: { - outputDir: 'karma-reports', - }, - webpack: { module: { rules: [ @@ -198,7 +195,7 @@ module.exports = function (config) { alias: { // alias any requires to the react module to the one in our // path, otherwise we tend to get the react source included - // twice when using npm link. + // twice when using `npm link` / `yarn link`. react: path.resolve('./node_modules/react'), 'matrix-react-sdk': path.resolve('test/skinned-sdk.js'), diff --git a/package.json b/package.json index 9ea3989f20..c409188125 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "0.14.8", + "version": "1.2.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -40,18 +40,19 @@ "rethemendex": "res/css/rethemendex.sh", "i18n": "matrix-gen-i18n", "prunei18n": "matrix-prune-i18n", - "build": "npm run reskindex && npm run start:init", + "build": "yarn reskindex && yarn start:init", "build:watch": "babel src -w --skip-initial-build -d lib --source-maps --copy-files", "emoji-data-strip": "node scripts/emoji-data-strip.js", - "start": "npm run start:init && npm run start:all", - "start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"npm run build:watch\" \"npm run reskindex:watch\"", + "start": "yarn start:init && yarn start:all", + "start:all": "concurrently --kill-others-on-fail --prefix \"{time} [{name}]\" -n build,reskindex \"yarn build:watch\" \"yarn reskindex:watch\"", "start:init": "babel src -d lib --source-maps --copy-files", "lint": "eslint src/", "lintall": "eslint src/ test/", "lintwithexclusions": "eslint --max-warnings 0 --ignore-path .eslintignore.errorfiles src test", + "stylelint": "stylelint res/css/**/*.scss", "clean": "rimraf lib", - "prepublish": "npm run clean && npm run build && git rev-parse HEAD > git-revision.txt", - "test": "karma start --single-run=true --browsers ChromeHeadless", + "prepare": "yarn clean && yarn build && git rev-parse HEAD > git-revision.txt", + "test": "karma start --single-run=true --browsers VectorChromeHeadless", "test-multi": "karma start" }, "dependencies": { @@ -64,7 +65,8 @@ "classnames": "^2.1.2", "commonmark": "^0.28.1", "counterpart": "^0.18.0", - "emojione": "2.2.7", + "emojibase-data": "^4.0.0", + "emojibase-regex": "^3.0.0", "file-saver": "^1.3.3", "filesize": "3.5.6", "flux": "2.1.1", @@ -73,17 +75,19 @@ "gemini-scrollbar": "github:matrix-org/gemini-scrollbar#b302279", "gfm.css": "^1.1.1", "glob": "^5.0.14", - "highlight.js": "^9.13.0", + "highlight.js": "9.14.2", "is-ip": "^2.0.0", "isomorphic-fetch": "^2.2.1", "linkifyjs": "^2.1.6", "lodash": "^4.13.1", "lolex": "2.3.2", - "matrix-js-sdk": "0.14.3", + "matrix-js-sdk": "1.2.0", "optimist": "^0.6.1", "pako": "^1.0.5", + "png-chunks-extract": "^1.0.0", "prop-types": "^15.5.8", "qrcode-react": "^0.1.16", + "qs": "^6.6.0", "querystring": "^0.2.0", "react": "^15.6.0", "react-addons-css-transition-group": "15.3.2", @@ -98,7 +102,7 @@ "slate-react": "^0.18.10", "text-encoding-utf-8": "^1.0.1", "url": "^0.11.0", - "velocity-vector": "github:vector-im/velocity#059e3b2", + "velocity-animate": "^1.5.2", "whatwg-fetch": "^1.1.1", "zxcvbn": "^4.4.2" }, @@ -109,6 +113,7 @@ "babel-loader": "^7.1.5", "babel-plugin-add-module-exports": "^0.2.1", "babel-plugin-transform-async-to-bluebird": "^1.1.1", + "babel-plugin-transform-builtin-extend": "^1.1.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-plugin-transform-runtime": "^6.23.0", @@ -117,7 +122,7 @@ "babel-preset-es2016": "^6.24.1", "babel-preset-es2017": "^6.24.1", "babel-preset-react": "^6.24.1", - "chokidar": "^1.6.1", + "chokidar": "^2.1.2", "concurrently": "^4.0.1", "eslint": "^5.12.0", "eslint-config-google": "^0.7.1", @@ -125,21 +130,20 @@ "eslint-plugin-flowtype": "^2.30.0", "eslint-plugin-react": "^7.7.0", "estree-walker": "^0.5.0", - "expect": "^23.6.0", + "expect": "^24.1.0", "file-loader": "^3.0.1", "flow-parser": "^0.57.3", "jest-mock": "^23.2.0", - "karma": "^3.0.0", - "karma-chrome-launcher": "^0.2.3", + "karma": "^4.0.1", + "karma-chrome-launcher": "^2.2.0", "karma-cli": "^1.0.1", - "karma-junit-reporter": "^0.4.2", "karma-logcapture-reporter": "0.0.1", "karma-mocha": "^1.3.0", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "^0.0.31", "karma-summary-reporter": "^1.5.1", "karma-webpack": "^4.0.0-beta.0", - "matrix-mock-request": "^1.2.1", + "matrix-mock-request": "^1.2.3", "matrix-react-test-utils": "^0.1.1", "mocha": "^5.0.5", "react-addons-test-utils": "^15.4.0", @@ -147,6 +151,8 @@ "rimraf": "^2.4.3", "sinon": "^5.0.7", "source-map-loader": "^0.2.3", + "stylelint": "^9.10.1", + "stylelint-config-standard": "^18.2.0", "walk": "^2.3.9", "webpack": "^4.20.2", "webpack-cli": "^3.1.1" diff --git a/res/css/_common.scss b/res/css/_common.scss index 56af1ab519..973103899d 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -32,10 +32,21 @@ body { margin: 0px; } +pre, code { + font-family: $monospace-font-family; + font-size: 100% !important; +} + .error, .warning { color: $warning-color; } +b { + // On Firefox, the default weight for `` is `bolder` which results in no bold + // effect since we only have specific weights of our fonts available. + font-weight: bold; +} + h2 { color: $primary-fg-color; font-weight: 400; @@ -50,8 +61,47 @@ a:visited { color: $accent-color-alt; } +input[type=text], +input[type=search], +input[type=password] { + padding: 9px; + font-family: $font-family; + font-size: 14px; + font-weight: 600; + min-width: 0; +} + +input[type=text].mx_textinput_icon, +input[type=search].mx_textinput_icon { + padding-left: 36px; + background-repeat: no-repeat; + background-position: 10px center; +} + +// FIXME THEME - Tint by CSS rather than referencing a duplicate asset +input[type=text].mx_textinput_icon.mx_textinput_search, +input[type=search].mx_textinput_icon.mx_textinput_search { + background-image: url('$(res)/img/feather-customised/search-input.svg'); +} + +// dont search UI as not all browsers support it, +// we implement it ourselves where needed instead +input[type=search]::-webkit-search-decoration, +input[type=search]::-webkit-search-cancel-button, +input[type=search]::-webkit-search-results-button, +input[type=search]::-webkit-search-results-decoration { + display: none; +} + +// Override Firefox's UA style so we get a consistent look across browsers +input::placeholder, +textarea::placeholder { + opacity: initial; +} + input[type=text], input[type=password], textarea { background-color: transparent; + color: $primary-fg-color; } input[type=text]:focus, input[type=password]:focus, textarea:focus { @@ -62,6 +112,80 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { /* Required by Firefox */ textarea { font-family: $font-family; + color: $primary-fg-color; +} + +// This is used to hide the standard outline added by browsers for +// accessible (focusable) components. Not intended for buttons, but +// should be used on things like focusable containers where the outline +// is usually not helping anyone. +.mx_HiddenFocusable { + outline: none; +} + +// .mx_textinput is a container for a text input +// + some other controls like buttons, ... +// it has the appearance of a text box so the controls +// appear to be part of the input + +.mx_Dialog, .mx_MatrixChat { + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + display: block; + box-sizing: border-box; + background-color: transparent; + color: $input-darker-fg-color; + border-radius: 4px; + border: 1px solid $dialog-close-fg-color; + // these things should probably not be defined + // globally + margin: 9px; + flex: 0 0 auto; + } + + .mx_textinput { + display: flex; + align-items: center; + + > input[type=text], + > input[type=search] { + border: none; + flex: 1; + color: $primary-fg-color; + } + } + + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text]::placeholder, + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search]::placeholder, + .mx_textinput input::placeholder { + color: $roomsublist-label-fg-color; + } +} + +/*** panels ***/ +.dark-panel { + background-color: $dark-panel-bg-color; +} + +.dark-panel { + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + color: $input-darker-fg-color; + background-color: $input-darker-bg-color; + border: none; + } +} + +.light-panel { + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], + :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], + .mx_textinput { + color: $input-lighter-fg-color; + background-color: $input-lighter-bg-color; + border: none; + } } /* Prevent ugly dotted highlight around selected elements in Firefox */ @@ -123,6 +247,17 @@ textarea { color: $roomsublist-label-bg-color; } +/* Expected z-indexes for dialogs: + 4000 - Default wrapper index + 4009 - Static dialog background + 4010 - Static dialog itself + 4011 - Standard dialog background + 4012 - Standard dialog itself + + These are set up such that the static dialog always appears + underneath the standard dialogs. + */ + .mx_Dialog_wrapper { position: fixed; z-index: 4000; @@ -144,27 +279,29 @@ textarea { box-shadow: none; } -/* View Source Dialog overide */ -.mx_Dialog_wrapper.mx_Dialog_viewsource .mx_Dialog { - padding-left: 10px; - padding-right: 10px; -} - .mx_Dialog { background-color: $primary-bg-color; color: $light-fg-color; - z-index: 4010; + z-index: 4012; font-weight: 300; font-size: 15px; position: relative; - padding: 40px 58px 36px 58px; - width: 60%; - max-width: 704px; - box-shadow: 2px 15px 30px 0 $dialog-shadow-color; + padding: 25px 30px 30px 30px; max-height: 80%; + box-shadow: 2px 15px 30px 0 $dialog-shadow-color; + border-radius: 4px; overflow-y: auto; } +.mx_Dialog_fixedWidth { + width: 60vw; + max-width: 704px; +} + +.mx_Dialog_staticWrapper .mx_Dialog { + z-index: 4010; +} + .mx_Dialog_background { position: fixed; top: 0; @@ -173,6 +310,17 @@ textarea { height: 100%; background-color: $dialog-backdrop-color; opacity: 0.8; + z-index: 4011; +} + +.mx_Dialog_background.mx_Dialog_staticBackground { + z-index: 4009; +} + +.mx_Dialog_wrapperWithStaticUnder .mx_Dialog_background { + // Roughly half of what it would normally be - we don't want to black out + // the app, just make it clear that the dialogs are stacked. + opacity: 0.4; } .mx_Dialog_lightbox .mx_Dialog_background { @@ -192,13 +340,13 @@ textarea { .mx_Dialog_header { position: relative; + margin-bottom: 20px; } .mx_Dialog_title { - font-weight: bold; font-size: 22px; line-height: 36px; - color: $primary-fg-color; + color: $dialog-title-fg-color; } .mx_Dialog_header.mx_Dialog_headerWithButton > .mx_Dialog_title { @@ -210,16 +358,17 @@ textarea { } .mx_Dialog_cancelButton { - mask: url('$(res)/img/feather-icons/cancel.svg'); + mask: url('$(res)/img/feather-customised/cancel.svg'); mask-repeat: no-repeat; mask-position: center; - width: 36px; - height: 36px; - background-color: $primary-fg-color; + mask-size: cover; + width: 14px; + height: 14px; + background-color: $dialog-close-fg-color; cursor: pointer; position: absolute; - top: 20px; - right: 20px; + top: 4px; + right: 0px; } .mx_Dialog_content { @@ -230,6 +379,7 @@ textarea { } .mx_Dialog_buttons { + margin-top: 20px; text-align: right; } @@ -242,7 +392,11 @@ textarea { font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; +} + +.mx_Dialog button:last-child { + margin-right: 0px; } .mx_Dialog button:hover, .mx_Dialog input[type="submit"]:hover { @@ -256,6 +410,7 @@ textarea { .mx_Dialog button.mx_Dialog_primary, .mx_Dialog input[type="submit"].mx_Dialog_primary { color: $accent-fg-color; background-color: $accent-color; + min-width: 156px; } .mx_Dialog button.danger, .mx_Dialog input[type="submit"].danger { @@ -270,6 +425,20 @@ textarea { opacity: 0.7; } +// TODO: Review mx_GeneralButton usage to see if it can use a different class +// These classes were brought in from the old UserSettings and are included here to avoid +// breaking the app. +// Ref: https://github.com/vector-im/riot-web/issues/8420 +.mx_GeneralButton { + @mixin mx_DialogButton; + display: inline; + margin: auto; +} + +.mx_GeneralButton:hover { + @mixin mx_DialogButton_hover; +} + .mx_linkButton { cursor: pointer; color: $accent-color; @@ -289,15 +458,6 @@ textarea { background-color: $primary-bg-color; } -.mx_emojione { - height: 1em; - vertical-align: middle; -} - -.mx_emojione_selected { - background-color: $accent-color; -} - ::-moz-selection { background-color: $accent-color; color: $selection-fg-color; @@ -367,3 +527,37 @@ textarea { opacity: 0; cursor: pointer; } + +// username colors +// used by SenderProfile & RoomPreviewBar +.mx_Username_color1 { + color: $username-variant1-color; +} + +.mx_Username_color2 { + color: $username-variant2-color; +} + +.mx_Username_color3 { + color: $username-variant3-color; +} + +.mx_Username_color4 { + color: $username-variant4-color; +} + +.mx_Username_color5 { + color: $username-variant5-color; +} + +.mx_Username_color6 { + color: $username-variant6-color; +} + +.mx_Username_color7 { + color: $username-variant7-color; +} + +.mx_Username_color8 { + color: $username-variant8-color; +} diff --git a/res/css/_components.scss b/res/css/_components.scss index ee55c000ff..2a91f08ee4 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -4,10 +4,14 @@ @import "./structures/_CompatibilityPage.scss"; @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; +@import "./structures/_CustomRoomTagPanel.scss"; @import "./structures/_FilePanel.scss"; +@import "./structures/_GenericErrorPage.scss"; @import "./structures/_GroupView.scss"; +@import "./structures/_HeaderButtons.scss"; @import "./structures/_HomePage.scss"; @import "./structures/_LeftPanel.scss"; +@import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; @import "./structures/_MyGroups.scss"; @import "./structures/_NotificationPanel.scss"; @@ -16,12 +20,13 @@ @import "./structures/_RoomStatusBar.scss"; @import "./structures/_RoomSubList.scss"; @import "./structures/_RoomView.scss"; +@import "./structures/_ScrollPanel.scss"; @import "./structures/_SearchBox.scss"; @import "./structures/_TabbedView.scss"; @import "./structures/_TagPanel.scss"; +@import "./structures/_TagPanelButtons.scss"; @import "./structures/_TopLeftMenuButton.scss"; @import "./structures/_UploadBar.scss"; -@import "./structures/_UserSettings.scss"; @import "./structures/_ViewSource.scss"; @import "./structures/auth/_Login.scss"; @import "./views/auth/_AuthBody.scss"; @@ -35,6 +40,7 @@ @import "./views/auth/_LanguageSelector.scss"; @import "./views/auth/_ServerConfig.scss"; @import "./views/auth/_ServerTypeSelector.scss"; +@import "./views/auth/_Welcome.scss"; @import "./views/avatars/_BaseAvatar.scss"; @import "./views/avatars/_MemberStatusMessageAvatar.scss"; @import "./views/context_menus/_MessageContextMenu.scss"; @@ -42,17 +48,20 @@ @import "./views/context_menus/_StatusMessageContextMenu.scss"; @import "./views/context_menus/_TagTileContextMenu.scss"; @import "./views/context_menus/_TopLeftMenu.scss"; +@import "./views/dialogs/_AddressPickerDialog.scss"; +@import "./views/dialogs/_Analytics.scss"; @import "./views/dialogs/_BugReportDialog.scss"; @import "./views/dialogs/_ChangelogDialog.scss"; @import "./views/dialogs/_ChatCreateOrReuseChatDialog.scss"; -@import "./views/dialogs/_ChatInviteDialog.scss"; @import "./views/dialogs/_ConfirmUserActionDialog.scss"; @import "./views/dialogs/_CreateGroupDialog.scss"; @import "./views/dialogs/_CreateRoomDialog.scss"; @import "./views/dialogs/_DeactivateAccountDialog.scss"; +@import "./views/dialogs/_DeviceVerifyDialog.scss"; @import "./views/dialogs/_DevtoolsDialog.scss"; @import "./views/dialogs/_EncryptedEventDialog.scss"; @import "./views/dialogs/_GroupAddressPicker.scss"; +@import "./views/dialogs/_IncomingSasDialog.scss"; @import "./views/dialogs/_RestoreKeyBackupDialog.scss"; @import "./views/dialogs/_RoomSettingsDialog.scss"; @import "./views/dialogs/_RoomUpgradeDialog.scss"; @@ -62,7 +71,9 @@ @import "./views/dialogs/_SettingsDialog.scss"; @import "./views/dialogs/_ShareDialog.scss"; @import "./views/dialogs/_UnknownDeviceDialog.scss"; +@import "./views/dialogs/_UploadConfirmDialog.scss"; @import "./views/dialogs/_UserSettingsDialog.scss"; +@import "./views/dialogs/_WidgetOpenIDPermissionsDialog.scss"; @import "./views/dialogs/keybackup/_CreateKeyBackupDialog.scss"; @import "./views/dialogs/keybackup/_KeyBackupFailedDialog.scss"; @import "./views/dialogs/keybackup/_RestoreKeyBackupDialog.scss"; @@ -74,10 +85,12 @@ @import "./views/elements/_Dropdown.scss"; @import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_Field.scss"; -@import "./views/elements/_HexVerify.scss"; @import "./views/elements/_ImageView.scss"; @import "./views/elements/_InlineSpinner.scss"; +@import "./views/elements/_ManageIntegsButton.scss"; @import "./views/elements/_MemberEventListSummary.scss"; +@import "./views/elements/_MessageEditor.scss"; +@import "./views/elements/_PowerSelector.scss"; @import "./views/elements/_ProgressBar.scss"; @import "./views/elements/_ReplyThread.scss"; @import "./views/elements/_ResizeHandle.scss"; @@ -85,8 +98,11 @@ @import "./views/elements/_RoleButton.scss"; @import "./views/elements/_Spinner.scss"; @import "./views/elements/_SyntaxHighlight.scss"; +@import "./views/elements/_TextWithTooltip.scss"; @import "./views/elements/_ToggleSwitch.scss"; @import "./views/elements/_ToolTipButton.scss"; +@import "./views/elements/_Tooltip.scss"; +@import "./views/elements/_Validation.scss"; @import "./views/globals/_MatrixToolbar.scss"; @import "./views/groups/_GroupPublicityToggle.scss"; @import "./views/groups/_GroupRoomList.scss"; @@ -99,11 +115,19 @@ @import "./views/messages/_MNoticeBody.scss"; @import "./views/messages/_MStickerBody.scss"; @import "./views/messages/_MTextBody.scss"; +@import "./views/messages/_MessageActionBar.scss"; @import "./views/messages/_MessageTimestamp.scss"; +@import "./views/messages/_ReactionDimension.scss"; +@import "./views/messages/_ReactionsRow.scss"; +@import "./views/messages/_ReactionsRowButton.scss"; +@import "./views/messages/_ReactionsRowButtonTooltip.scss"; @import "./views/messages/_RoomAvatarEvent.scss"; @import "./views/messages/_SenderProfile.scss"; @import "./views/messages/_TextualEvent.scss"; @import "./views/messages/_UnknownBody.scss"; +@import "./views/messages/_ViewSourceEvent.scss"; +@import "./views/room_settings/_AliasSettings.scss"; +@import "./views/room_settings/_ColorSettings.scss"; @import "./views/rooms/_AppsDrawer.scss"; @import "./views/rooms/_Autocomplete.scss"; @import "./views/rooms/_AuxPanel.scss"; @@ -120,14 +144,13 @@ @import "./views/rooms/_PinnedEventsPanel.scss"; @import "./views/rooms/_PresenceLabel.scss"; @import "./views/rooms/_ReplyPreview.scss"; +@import "./views/rooms/_RoomBreadcrumbs.scss"; @import "./views/rooms/_RoomDropTarget.scss"; @import "./views/rooms/_RoomHeader.scss"; @import "./views/rooms/_RoomList.scss"; @import "./views/rooms/_RoomPreviewBar.scss"; @import "./views/rooms/_RoomRecoveryReminder.scss"; -@import "./views/rooms/_RoomSettings.scss"; @import "./views/rooms/_RoomTile.scss"; -@import "./views/rooms/_RoomTooltip.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SearchableEntityList.scss"; @@ -141,16 +164,17 @@ @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_ProfileSettings.scss"; -@import "./views/settings/tabs/_GeneralRoomSettingsTab.scss"; -@import "./views/settings/tabs/_GeneralUserSettingsTab.scss"; -@import "./views/settings/tabs/_HelpSettingsTab.scss"; -@import "./views/settings/tabs/_NotificationSettingsTab.scss"; -@import "./views/settings/tabs/_PreferencesSettingsTab.scss"; -@import "./views/settings/tabs/_RolesRoomSettingsTab.scss"; -@import "./views/settings/tabs/_SecurityRoomSettingsTab.scss"; -@import "./views/settings/tabs/_SecuritySettingsTab.scss"; @import "./views/settings/tabs/_SettingsTab.scss"; -@import "./views/settings/tabs/_VoiceSettingsTab.scss"; +@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.scss"; +@import "./views/settings/tabs/room/_RolesRoomSettingsTab.scss"; +@import "./views/settings/tabs/room/_SecurityRoomSettingsTab.scss"; +@import "./views/settings/tabs/user/_GeneralUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_HelpUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_NotificationUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_PreferencesUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_SecurityUserSettingsTab.scss"; +@import "./views/settings/tabs/user/_VoiceUserSettingsTab.scss"; +@import "./views/verification/_VerificationShowSas.scss"; @import "./views/voip/_CallView.scss"; @import "./views/voip/_IncomingCallbox.scss"; @import "./views/voip/_VideoView.scss"; diff --git a/res/css/structures/_AutoHideScrollbar.scss b/res/css/structures/_AutoHideScrollbar.scss index cb0d6f7f09..db86a6fbd6 100644 --- a/res/css/structures/_AutoHideScrollbar.scss +++ b/res/css/structures/_AutoHideScrollbar.scss @@ -14,6 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* This file has CSS for both native and non-native scrollbars in an + * order that's fairly logic to read but violates stylelints descending + * specificity rule, so turn it off for this file. It also duplicates + * a selector to separate the hiding/showing from the sizing. + */ +/* stylelint-disable no-descending-specificity, no-duplicate-selectors */ + /* 1. for browsers that support native overlay auto-hiding scrollbars */ @@ -59,8 +66,7 @@ body.mx_scrollbar_nooverlay { */ .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow > .mx_AutoHideScrollbar_offset, .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::before, - .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::after - { + .mx_AutoHideScrollbar:hover.mx_AutoHideScrollbar_overflow::after { margin-right: calc(-1 * var(--scrollbar-width)); } } @@ -74,6 +80,7 @@ body.mx_scrollbar_nooverlay { // or fallback for webkit browsers ::-webkit-scrollbar { width: 6px; + height: 6px; background-color: $scrollbar-track-color; } diff --git a/res/css/structures/_CompatibilityPage.scss b/res/css/structures/_CompatibilityPage.scss index f3f032c975..26354ed124 100644 --- a/res/css/structures/_CompatibilityPage.scss +++ b/res/css/structures/_CompatibilityPage.scss @@ -16,4 +16,4 @@ border: 1px solid; padding: 10px; background-color: #fcc; -} \ No newline at end of file +} diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index 001c405e15..fc1538a13d 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -31,11 +31,11 @@ limitations under the License. .mx_ContextualMenu { border-radius: 4px; - box-shadow: 4px 4px 12px 0 rgba(118, 131, 156, 0.6);; + box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; background-color: $menu-bg-color; color: $primary-fg-color; position: absolute; - font-size: 14px; + font-size: 14px; z-index: 5001; } @@ -54,14 +54,14 @@ limitations under the License. border-bottom: 8px solid transparent; } -.mx_ContextualMenu_chevron_right:after { - content:''; +.mx_ContextualMenu_chevron_right::after { + content: ''; width: 0; height: 0; border-top: 7px solid transparent; border-left: 7px solid $menu-bg-color; border-bottom: 7px solid transparent; - position:absolute; + position: absolute; top: -7px; right: 1px; } @@ -81,14 +81,14 @@ limitations under the License. border-bottom: 8px solid transparent; } -.mx_ContextualMenu_chevron_left:after{ - content:''; +.mx_ContextualMenu_chevron_left::after { + content: ''; width: 0; height: 0; border-top: 7px solid transparent; border-right: 7px solid $menu-bg-color; border-bottom: 7px solid transparent; - position:absolute; + position: absolute; top: -7px; left: 1px; } @@ -108,14 +108,14 @@ limitations under the License. border-right: 8px solid transparent; } -.mx_ContextualMenu_chevron_top:after{ - content:''; +.mx_ContextualMenu_chevron_top::after { + content: ''; width: 0; height: 0; border-left: 7px solid transparent; border-bottom: 7px solid $menu-bg-color; border-right: 7px solid transparent; - position:absolute; + position: absolute; left: -7px; top: 1px; } @@ -135,14 +135,14 @@ limitations under the License. border-right: 8px solid transparent; } -.mx_ContextualMenu_chevron_bottom:after{ - content:''; +.mx_ContextualMenu_chevron_bottom::after { + content: ''; width: 0; height: 0; border-left: 7px solid transparent; border-top: 7px solid $menu-bg-color; border-right: 7px solid transparent; - position:absolute; + position: absolute; left: -7px; bottom: 1px; } diff --git a/res/css/structures/_CreateRoom.scss b/res/css/structures/_CreateRoom.scss index 2be193525e..10f9e23a02 100644 --- a/res/css/structures/_CreateRoom.scss +++ b/res/css/structures/_CreateRoom.scss @@ -22,7 +22,7 @@ limitations under the License. } .mx_CreateRoom input, -.mx_CreateRoom textarea { +.mx_CreateRoom textarea { border-radius: 3px; border: 1px solid $strong-input-border-color; font-weight: 300; diff --git a/res/css/structures/_CustomRoomTagPanel.scss b/res/css/structures/_CustomRoomTagPanel.scss new file mode 100644 index 0000000000..45961d7be1 --- /dev/null +++ b/res/css/structures/_CustomRoomTagPanel.scss @@ -0,0 +1,45 @@ +/* +Copyright 2019 New Vector Ltd. + +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_LeftPanel_tagPanelContainer { + display: flex; + flex-direction: column; +} + +.mx_CustomRoomTagPanel { + background-color: $tagpanel-bg-color; + max-height: 40vh; +} + +.mx_CustomRoomTagPanel_scroller { + max-height: inherit; +} + +.mx_CustomRoomTagPanel .mx_AccessibleButton { + margin: 9px auto; + width: 40px; +} + +.mx_CustomRoomTagPanel .mx_BaseAvatar_image { + box-sizing: border-box; + width: 40px; + height: 40px; +} + +.mx_CustomRoomTagPanel .mx_AccessibleButton.CustomRoomTagPanel_tileSelected .mx_BaseAvatar_image { + border: 3px solid $warning-color; + border-radius: 40px; +} diff --git a/res/css/structures/_FilePanel.scss b/res/css/structures/_FilePanel.scss index 677fa34c6f..703e90f402 100644 --- a/res/css/structures/_FilePanel.scss +++ b/res/css/structures/_FilePanel.scss @@ -101,10 +101,10 @@ limitations under the License. padding-left: 0px; } -.mx_FilePanel .mx_EventTile:hover .mx_EventTile_line { - background-color: $primary-bg-color; -} - .mx_FilePanel .mx_EventTile_selected .mx_EventTile_line { padding-left: 0px; } + +.mx_FilePanel .mx_EventTile:hover .mx_EventTile_line { + background-color: $primary-bg-color; +} diff --git a/res/css/structures/_GenericErrorPage.scss b/res/css/structures/_GenericErrorPage.scss new file mode 100644 index 0000000000..9c973af411 --- /dev/null +++ b/res/css/structures/_GenericErrorPage.scss @@ -0,0 +1,19 @@ +.mx_GenericErrorPage { + width: 100%; + height: 100%; + background-color: #fff; +} + +.mx_GenericErrorPage_box { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + margin: auto; + width: 500px; + height: 200px; + border: 1px solid #f22; + padding: 10px; + background-color: #fcc; +} diff --git a/res/css/structures/_GroupView.scss b/res/css/structures/_GroupView.scss index ace310ee5b..ae86f68fd0 100644 --- a/res/css/structures/_GroupView.scss +++ b/res/css/structures/_GroupView.scss @@ -44,23 +44,36 @@ limitations under the License. } .mx_GroupHeader_button { - margin-left: 12px; + margin-left: 5px; + margin-right: 5px; cursor: pointer; + height: 20px; + width: 20px; + background-color: $groupheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_GroupHeader_button object { - // prevents clicks from being swallowed by svg in 'object' tag - pointer-events: none; +.mx_GroupHeader_editButton { + mask-image: url('$(res)/img/icons-settings-room.svg'); +} + +.mx_GroupHeader_shareButton { + mask-image: url('$(res)/img/icons-share.svg'); +} + +.mx_GroupView_hostingSignup img { + margin-left: 5px; } .mx_GroupView_editable { - border-bottom: 1px solid $strong-input-border-color ! important; + border-bottom: 1px solid $strong-input-border-color !important; min-width: 150px; cursor: text; } .mx_GroupView_editable:focus { - border-bottom: 1px solid $accent-color ! important; + border-bottom: 1px solid $accent-color !important; outline: none; box-shadow: none; } @@ -82,7 +95,7 @@ limitations under the License. .mx_GroupView_avatarPicker .mx_Spinner { width: 48px; - height: 48px ! important; + height: 48px !important; } .mx_GroupView_header_leftCol { @@ -161,10 +174,9 @@ limitations under the License. .mx_GroupView > .mx_MainSplit { flex: 1; - display: flex; } -.mx_GroupView_body { +.mx_GroupView_body { flex-grow: 1; } @@ -321,7 +333,7 @@ limitations under the License. display: none; } -.mx_GroupView_body .gm-scroll-view > *{ +.mx_GroupView_body .gm-scroll-view > * { margin: 11px 50px 0px 68px; } diff --git a/res/css/structures/_HeaderButtons.scss b/res/css/structures/_HeaderButtons.scss new file mode 100644 index 0000000000..eef7653b24 --- /dev/null +++ b/res/css/structures/_HeaderButtons.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 New Vector Ltd + +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_HeaderButtons { + display: flex; +} + +.mx_HeaderButtons::before { + content: ""; + background-color: $header-divider-color; + opacity: 0.5; + margin: 0 15px; + border-radius: 1px; + width: 1px; +} diff --git a/res/css/structures/_HomePage.scss b/res/css/structures/_HomePage.scss index 5f1e035e99..3aa80f6f59 100644 --- a/res/css/structures/_HomePage.scss +++ b/res/css/structures/_HomePage.scss @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -22,10 +23,3 @@ limitations under the License. margin-left: auto; margin-right: auto; } - -.mx_HomePage iframe { - display: block; - width: 100%; - height: 100%; - border: 0px; -} diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index 941417eccc..a8d8669285 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -33,6 +33,11 @@ limitations under the License. flex: 0 0 140px; } +.mx_LeftPanel_tagPanelContainer { + flex: 0 0 70px; + height: 100%; +} + .mx_LeftPanel_hideButton { position: absolute; top: 10px; @@ -62,11 +67,6 @@ limitations under the License. z-index: 6; } -.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu { - flex: 0 0 160px; - margin-bottom: 9px; -} - .mx_LeftPanel .mx_BottomLeftMenu { order: 3; @@ -77,6 +77,11 @@ limitations under the License. z-index: 1; } +.mx_LeftPanel_container.collapsed .mx_BottomLeftMenu { + flex: 0 0 160px; + margin-bottom: 9px; +} + .mx_LeftPanel .mx_BottomLeftMenu_options { margin-top: 18px; } diff --git a/res/css/structures/_MainSplit.scss b/res/css/structures/_MainSplit.scss new file mode 100644 index 0000000000..4d73953cd7 --- /dev/null +++ b/res/css/structures/_MainSplit.scss @@ -0,0 +1,27 @@ +/* +Copyright 2019 New Vector Ltd + +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_MainSplit { + display: flex; + flex-direction: row; + min-width: 0; +} + +// move hit area 5px to the right so it doesn't overlap with the timeline scrollbar +.mx_MainSplit > .mx_ResizeHandle.mx_ResizeHandle_horizontal { + margin: 0 -10px 0 0; + padding: 0 10px 0 0; +} diff --git a/res/css/structures/_MyGroups.scss b/res/css/structures/_MyGroups.scss index b3a5c4f473..d25789ab94 100644 --- a/res/css/structures/_MyGroups.scss +++ b/res/css/structures/_MyGroups.scss @@ -49,11 +49,11 @@ limitations under the License. height: 40px; width: 40px; border-radius: 20px; - background-color: $roomheader-addroom-color; + background-color: $roomheader-addroom-bg-color; position: relative; - &:before { - background-color: $accent-fg-color; + &::before { + background-color: $roomheader-addroom-fg-color; mask: url('$(res)/img/icons-create-room.svg'); mask-repeat: no-repeat; mask-position: center; @@ -113,8 +113,7 @@ limitations under the License. overflow-x: hidden; display: flex; - flex-direction: row; - flex-flow: wrap; + flex-flow: row wrap; align-content: flex-start; } @@ -153,6 +152,7 @@ limitations under the License. .mx_GroupTile_profile .mx_GroupTile_groupId { font-size: 13px; + opacity: 0.7; } .mx_GroupTile_profile .mx_GroupTile_desc { @@ -163,7 +163,3 @@ limitations under the License. max-height: 36px; overflow: hidden; } - -.mx_GroupTile_profile .mx_GroupTile_groupId { - opacity: 0.7; -} diff --git a/res/css/structures/_NotificationPanel.scss b/res/css/structures/_NotificationPanel.scss index b171aa3e36..78b3522d4e 100644 --- a/res/css/structures/_NotificationPanel.scss +++ b/res/css/structures/_NotificationPanel.scss @@ -66,7 +66,7 @@ limitations under the License. .mx_NotificationPanel .mx_EventTile_roomName a, .mx_NotificationPanel .mx_EventTile_senderDetails a { - text-decoration: none ! important; + text-decoration: none !important; } .mx_NotificationPanel .mx_EventTile .mx_MessageTimestamp { @@ -83,14 +83,14 @@ limitations under the License. padding-right: 0px; } -.mx_NotificationPanel .mx_EventTile:hover .mx_EventTile_line { - background-color: $primary-bg-color; -} - .mx_NotificationPanel .mx_EventTile_selected .mx_EventTile_line { padding-left: 0px; } +.mx_NotificationPanel .mx_EventTile:hover .mx_EventTile_line { + background-color: $primary-bg-color; +} + .mx_NotificationPanel .mx_EventTile_content { margin-right: 0px; } diff --git a/res/css/structures/_RightPanel.scss b/res/css/structures/_RightPanel.scss index 592eea067e..c63db5d274 100644 --- a/res/css/structures/_RightPanel.scss +++ b/res/css/structures/_RightPanel.scss @@ -25,9 +25,7 @@ limitations under the License. .mx_RightPanel_header { order: 1; - border-bottom: 1px solid $primary-hairline-color; - flex: 0 0 52px; } @@ -45,20 +43,55 @@ limitations under the License. cursor: pointer; flex: 0 0 auto; vertical-align: top; - margin-top: 4px; - padding-left: 5px; - padding-right: 5px; + margin-left: 5px; + margin-right: 5px; text-align: center; - position: relative; border-bottom: 2px solid transparent; + height: 20px; + width: 20px; + position: relative; } -.mx_RightPanel_headerButton object { - pointer-events: none; +.mx_RightPanel_headerButton::before { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 20px; + width: 20px; + background-color: $rightpanel-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_RightPanel_headerButton_highlight { - border-color: $button-bg-color; +.mx_RightPanel_membersButton::before { + mask-image: url('$(res)/img/feather-customised/user.svg'); +} + +.mx_RightPanel_filesButton::before { + mask-image: url('$(res)/img/feather-customised/files.svg'); +} + +.mx_RightPanel_notifsButton::before { + mask-image: url('$(res)/img/feather-customised/notifications.svg'); +} + +.mx_RightPanel_groupMembersButton::before { + mask-image: url('$(res)/img/icons-people.svg'); +} + +.mx_RightPanel_roomsButton::before { + mask-image: url('$(res)/img/icons-room-nobg.svg'); +} + +.mx_RightPanel_headerButton_highlight::after { + content: ''; + position: absolute; + bottom: -6px; + left: 0; + right: 0; + height: 2px; + background-color: $button-bg-color; } .mx_RightPanel_headerButton_badge { diff --git a/res/css/structures/_RoomStatusBar.scss b/res/css/structures/_RoomStatusBar.scss index 1054654670..090a40235f 100644 --- a/res/css/structures/_RoomStatusBar.scss +++ b/res/css/structures/_RoomStatusBar.scss @@ -40,12 +40,12 @@ limitations under the License. opacity: 0.5; position: relative; top: -4px; -/* + /* animation-duration: 1s; animation-name: bounce; animation-direction: alternate; animation-iteration-count: infinite; -*/ + */ } .mx_RoomStatusBar_placeholderIndicator span:nth-child(1) { @@ -138,8 +138,8 @@ limitations under the License. } .mx_RoomStatusBar_resend_link { - color: $primary-fg-color ! important; - text-decoration: underline ! important; + color: $primary-fg-color !important; + text-decoration: underline !important; cursor: pointer; } @@ -173,12 +173,12 @@ limitations under the License. } .mx_RoomStatusBar_callBar { - height: 40px; - line-height: 40px; + height: 40px; + line-height: 40px; } .mx_RoomStatusBar_typingBar { - height: 40px; - line-height: 40px; + height: 40px; + line-height: 40px; } } diff --git a/res/css/structures/_RoomSubList.scss b/res/css/structures/_RoomSubList.scss index eb056a5eff..15fddba817 100644 --- a/res/css/structures/_RoomSubList.scss +++ b/res/css/structures/_RoomSubList.scss @@ -75,6 +75,7 @@ limitations under the License. font-size: 12px; padding: 0 5px; background-color: $roomtile-name-color; + cursor: pointer; } .mx_RoomSubList_addRoom, .mx_RoomSubList_badge { @@ -82,15 +83,24 @@ limitations under the License. } .mx_RoomSubList_addRoom { - background-color: $roomheader-addroom-color; - color: $roomsublist-background; - background-image: url('$(res)/img/icons-room-add.svg'); - background-repeat: no-repeat; - background-position: center; + background-color: $roomheader-addroom-bg-color; border-radius: 10px; // 16/2 + 2 padding height: 16px; flex: 0 0 16px; - background-clip: content-box; + position: relative; + + &::before { + background-color: $roomheader-addroom-fg-color; + mask: url('$(res)/img/icons-room-add.svg'); + mask-repeat: no-repeat; + mask-position: center; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } } .mx_RoomSubList_badgeHighlight { @@ -99,7 +109,7 @@ limitations under the License. .mx_RoomSubList_chevron { pointer-events: none; - mask: url('$(res)/img/feather-icons/dropdown-arrow.svg'); + mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; transition: transform 0.2s ease-in; width: 10px; @@ -127,6 +137,26 @@ limitations under the License. padding: 0 8px; } +.collapsed { + .mx_RoomSubList_scroll { + padding: 0; + } + + .mx_RoomSubList_labelContainer { + margin-right: 14px; + margin-left: 2px; + } + + .mx_RoomSubList_addRoom { + margin-left: 3px; + margin-right: 10px; + } + + .mx_RoomSubList_label > span { + display: none; + } +} + // overflow indicators .mx_RoomSubList:not(.resized-all) > .mx_RoomSubList_scroll { &.mx_IndicatorScrollbar_topOverflow::before, @@ -151,10 +181,10 @@ limitations under the License. &.mx_IndicatorScrollbar_topOverflow::before { top: 0; transition: background-image 0.1s ease-in; - background: linear-gradient(to top, rgba(242,245,248,0), rgba(242,245,248,1)); + background: linear-gradient(to top, $panel-gradient); } -/* + /* // for now, we remove the bottomOverflow entirely as we don't want to // lose the screen real-estate due to a bg-colored gradient, but we also // don't want to use drop shadows and risk a confusing hierarchy of cards. @@ -165,26 +195,5 @@ limitations under the License. margin: 0px -8px; background: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.0)); } -*/ -} - -.collapsed { - - .mx_RoomSubList_scroll { - padding: 0; - } - - .mx_RoomSubList_labelContainer { - margin-right: 14px; - margin-left: 2px; - } - - .mx_RoomSubList_addRoom { - margin-left: 3px; - margin-right: 10px; - } - - .mx_RoomSubList_label > span { - display: none; - } + */ } diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 77b868e391..50d412ad58 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -70,48 +70,27 @@ limitations under the License. background-color: $primary-bg-color; } +.mx_RoomView_auxPanel_hiddenHighlights { + border-bottom: 1px solid $primary-hairline-color; + padding: 10px 26px; + color: $warning-color; + cursor: pointer; +} + .mx_RoomView_auxPanel_apps { - max-width: 1920px ! important; + max-width: 1920px !important; } .mx_RoomView .mx_MainSplit { flex: 1 1 0; - display: flex; -} - -.mx_RoomView_body { - position: relative; //for .mx_RoomView_auxPanel_fullHeight - display: flex; - flex-direction: column; - flex: 1; -} - -.mx_RoomView_body .mx_RoomView_timeline { - /* offset parent for mx_RoomView_topUnreadMessagesBar */ - position: relative; - flex: 1; - display: flex; - flex-direction: column; -} - -.mx_RoomView_body { - .mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner{ - order: 2; - } -} - -.mx_RoomView_body .mx_RoomView_statusArea { - order: 3; -} - -.mx_RoomView_body .mx_MessageComposer { - order: 4; } .mx_RoomView_messagePanel { width: 100%; overflow-y: auto; + flex: 1 1 0; + overflow-anchor: none; } .mx_RoomView_messagePanelSearchSpinner { @@ -123,9 +102,9 @@ limitations under the License. position: relative; } -.mx_RoomView_messagePanelSearchSpinner:before { +.mx_RoomView_messagePanelSearchSpinner::before { background-color: $greyed-fg-color; - mask: url('$(res)/img/feather-icons/search-input.svg'); + mask: url('$(res)/img/feather-customised/search-input.svg'); mask-repeat: no-repeat; mask-position: center; mask-size: 50px; @@ -137,6 +116,53 @@ limitations under the License. height: 50px; } +.mx_RoomView_body { + position: relative; //for .mx_RoomView_auxPanel_fullHeight + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; + + .mx_RoomView_messagePanel, .mx_RoomView_messagePanelSpinner, .mx_RoomView_messagePanelSearchSpinner { + order: 2; + } +} + +.mx_RoomView_body .mx_RoomView_timeline { + /* offset parent for mx_RoomView_topUnreadMessagesBar */ + position: relative; + flex: 1; + display: flex; + flex-direction: column; +} + +.mx_RoomView_statusArea { + width: 100%; + flex: 0 0 auto; + + max-height: 0px; + background-color: $primary-bg-color; + z-index: 1000; + overflow: hidden; + + transition: all .2s ease-out; +} + +.mx_RoomView_statusArea_expanded { + max-height: 100px; +} + +.mx_RoomView_statusAreaBox { + margin: auto; + min-height: 50px; +} + +.mx_RoomView_statusAreaBox_line { + margin-left: 65px; + border-top: 1px solid $primary-hairline-color; + height: 1px; +} + .mx_RoomView_messageListWrapper { min-height: 100%; @@ -197,33 +223,6 @@ hr.mx_RoomView_myReadMarker { z-index: 1; } -.mx_RoomView_statusArea { - width: 100%; - flex: 0 0 auto; - - max-height: 0px; - background-color: $primary-bg-color; - z-index: 1000; - overflow: hidden; - - transition: all .2s ease-out; -} - -.mx_RoomView_statusArea_expanded { - max-height: 100px; -} - -.mx_RoomView_statusAreaBox { - margin: auto; - min-height: 50px; -} - -.mx_RoomView_statusAreaBox_line { - margin-left: 65px; - border-top: 1px solid $primary-hairline-color; - height: 1px; -} - .mx_RoomView_callStatusBar .mx_UploadBar_uploadProgressInner { background-color: $primary-bg-color; } @@ -284,7 +283,7 @@ hr.mx_RoomView_myReadMarker { } .mx_RoomView_ongoingConfCallNotification a { - color: $accent-fg-color ! important; + color: $accent-fg-color !important; } .mx_MatrixChat_useCompactLayout { diff --git a/res/css/structures/_ScrollPanel.scss b/res/css/structures/_ScrollPanel.scss new file mode 100644 index 0000000000..699224949b --- /dev/null +++ b/res/css/structures/_ScrollPanel.scss @@ -0,0 +1,26 @@ +/* +Copyright 2019 New Vector Ltd + +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_ScrollPanel { + + .mx_RoomView_MessageList { + position: relative; + display: flex; + flex-direction: column; + justify-content: flex-end; + overflow-y: hidden; + } +} diff --git a/res/css/structures/_TabbedView.scss b/res/css/structures/_TabbedView.scss index c87b4d053f..7904df5a82 100644 --- a/res/css/structures/_TabbedView.scss +++ b/res/css/structures/_TabbedView.scss @@ -28,8 +28,8 @@ limitations under the License. } .mx_TabbedView_tabLabels { - width: 150px; - max-width: 150px; + width: 170px; + max-width: 170px; color: $tab-label-fg-color; position: fixed; } @@ -39,9 +39,8 @@ limitations under the License. cursor: pointer; display: block; border-radius: 3px; - font-size: 12px; - font-weight: 600; - min-height: 20px; // use min-height instead of height to allow the label to overflow a bit + font-size: 14px; + min-height: 24px; // use min-height instead of height to allow the label to overflow a bit margin-bottom: 6px; position: relative; } @@ -51,33 +50,28 @@ limitations under the License. color: $tab-label-active-fg-color; } -// TODO: Remove temporary hack alongside "visit old settings" tab -.mx_TabbedView_tabLabel_TEMP_HACK { - background-color: orange; -} - -.mx_TabbedView_maskedIcon {; +.mx_TabbedView_maskedIcon { margin-left: 6px; margin-right: 9px; margin-top: 1px; - width: 14px; - height: 14px; + width: 16px; + height: 16px; display: inline-block; } -.mx_TabbedView_maskedIcon:before { +.mx_TabbedView_maskedIcon::before { display: inline-block; background-color: $tab-label-icon-bg-color; mask-repeat: no-repeat; - mask-size: 14px; - width: 14px; - height: 18px; + mask-size: 16px; + width: 16px; + height: 22px; mask-position: center; content: ''; vertical-align: middle; } -.mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon:before { +.mx_TabbedView_tabLabel_active .mx_TabbedView_maskedIcon::before { background-color: $tab-label-active-icon-bg-color; } @@ -86,14 +80,15 @@ limitations under the License. } .mx_TabbedView_tabPanel { - margin-left: 220px; // 150px sidebar + 70px padding + margin-left: 240px; // 170px sidebar + 70px padding flex-grow: 1; display: flex; flex-direction: column; + min-height: 0; // firefox } .mx_TabbedView_tabPanelContent { flex-grow: 1; overflow: auto; min-height: 0; // firefox -} \ No newline at end of file +} diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index bf1746bd0e..a818f52125 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -15,7 +15,7 @@ limitations under the License. */ .mx_TagPanel { - flex: 0 0 70px; + flex: 1; background-color: $tagpanel-bg-color; cursor: pointer; @@ -36,7 +36,6 @@ limitations under the License. flex: none; - display: flex; justify-content: center; align-items: flex-start; @@ -68,26 +67,23 @@ limitations under the License. height: 100%; } +.mx_TagPanel .mx_TagPanel_tagTileContainer > div { + height: 40px; + padding: 5px 0 4px 0; +} .mx_TagPanel .mx_TagTile { - padding-top: 9px; - padding-bottom: 9px; -// opacity: 0.5; + margin: 9px 0; + // opacity: 0.5; position: relative; } .mx_TagPanel .mx_TagTile:focus, .mx_TagPanel .mx_TagTile:hover, .mx_TagPanel .mx_TagTile.mx_TagTile_selected { -// opacity: 1; -} - -.mx_TagPanel .mx_TagTile.mx_TagTile_selected { - /* To offset border of mx_TagTile_avatar */ - padding: 3px 0px; + // opacity: 1; } .mx_TagPanel .mx_TagTile.mx_TagTile_selected .mx_TagTile_avatar .mx_BaseAvatar { - border: 3px solid $accent-color; background-color: $accent-color; border-radius: 40px; @@ -97,6 +93,13 @@ limitations under the License. width: 40px; } +.mx_TagPanel .mx_TagTile_selected .mx_BaseAvatar_image { + border: 3px solid $accent-color; + height: 40px; + width: 40px; + box-sizing: border-box; +} + .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { filter: none; } @@ -112,7 +115,7 @@ limitations under the License. height: 15px; position: absolute; right: -5px; - top: 1px; + top: -8px; border-radius: 8px; background-color: $neutral-badge-color; color: #ffffff; @@ -124,39 +127,22 @@ limitations under the License. padding-right: 4px; } -.mx_TagPanel_groupsButton { - flex: 0; - margin: 17px 0 3px 0; -} - -.mx_TagPanel_groupsButton > .mx_GroupsButton:before { - mask: url('$(res)/img/feather-icons/users.svg'); - mask-position: center 11px; -} - -.mx_TagPanel_groupsButton > .mx_TagPanel_report:before { - mask: url('$(res)/img/feather-icons/life-buoy.svg'); - mask-position: center 9px; -} - -.mx_TagPanel_groupsButton > .mx_AccessibleButton { - margin-bottom: 12px; - height: 40px; - width: 40px; - border-radius: 20px; - background-color: $roomheader-addroom-color; +.mx_TagTile_avatar { position: relative; - /* overwrite mx_RoleButton inline-block */ - display: block !important; - - &:before { - background-color: $tagpanel-bg-color; - mask-repeat: no-repeat; - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - } +} + +.mx_TagTile_badge { + position: absolute; + right: -4px; + top: -2px; + border-radius: 8px; + color: $accent-fg-color; + font-weight: 600; + font-size: 14px; + padding: 0 5px; + background-color: $roomtile-name-color; +} + +.mx_TagTile_badgeHighlight { + background-color: $warning-color; } diff --git a/res/css/structures/_TagPanelButtons.scss b/res/css/structures/_TagPanelButtons.scss new file mode 100644 index 0000000000..70fea92959 --- /dev/null +++ b/res/css/structures/_TagPanelButtons.scss @@ -0,0 +1,56 @@ +/* +Copyright 2019 New Vector Ltd. + +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_TagPanelButtons { + background-color: $tagpanel-bg-color; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 17px 0 3px 0; +} + +.mx_TagPanelButtons > .mx_GroupsButton::before { + mask: url('$(res)/img/feather-customised/users.svg'); + mask-position: center 11px; +} + +.mx_TagPanelButtons > .mx_TagPanelButtons_report::before { + mask: url('$(res)/img/feather-customised/life-buoy.svg'); + mask-position: center 9px; +} + +.mx_TagPanelButtons > .mx_AccessibleButton { + margin-bottom: 12px; + height: 40px; + width: 40px; + border-radius: 20px; + background-color: $tagpanel-button-color; + position: relative; + /* overwrite mx_RoleButton inline-block */ + display: block !important; + + &::before { + background-color: $tagpanel-bg-color; + mask-repeat: no-repeat; + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } +} diff --git a/res/css/structures/_TopLeftMenuButton.scss b/res/css/structures/_TopLeftMenuButton.scss index e72f1f16da..94a391ae70 100644 --- a/res/css/structures/_TopLeftMenuButton.scss +++ b/res/css/structures/_TopLeftMenuButton.scss @@ -41,7 +41,7 @@ limitations under the License. .mx_TopLeftMenuButton_chevron { margin: 0 7px; - mask: url('$(res)/img/feather-icons/dropdown-arrow.svg'); + mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; width: 10px; height: 6px; diff --git a/res/css/structures/_UserSettings.scss b/res/css/structures/_UserSettings.scss deleted file mode 100644 index b078a4e242..0000000000 --- a/res/css/structures/_UserSettings.scss +++ /dev/null @@ -1,269 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd - -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_UserSettings { - max-width: 960px; - width: 100%; - margin-left: auto; - margin-right: auto; - - display: flex; - flex-direction: column; -} - -.mx_UserSettings .mx_RoomHeader { - order: 1; - - flex: 0 0 70px; -} - -.mx_UserSettings_body { - order: 2; - - flex: 1 1 0; - - margin-top: -20px; - overflow-y: auto; -} - -.mx_UserSettings h3 { - clear: both; - margin-left: 63px; - text-transform: uppercase; - color: $h3-color; - font-weight: 600; - font-size: 13px; - margin-top: 26px; - margin-bottom: 10px; -} - -.mx_UserSettings_section h3 { - margin-left: 0px; -} - -.mx_UserSettings_spinner { - display: inline-block; - vertical-align: middle; - margin-right: 12px; - width: 32px; - height: 32px; -} - -.mx_UserSettings_button { - @mixin mx_DialogButton; - display: inline; - margin: auto; -} - -.mx_UserSettings_button:hover { - @mixin mx_DialogButton_hover; -} - -.mx_UserSettings_button.danger { - background-color: $warning-color; -} - -.mx_UserSettings_section { - margin-left: 63px; - margin-top: 28px; - margin-bottom: 28px; -} - -.mx_UserSettings_cryptoSection ul { - display: table; -} -.mx_UserSettings_cryptoSection li { - display: table-row; -} -.mx_UserSettings_cryptoSection label, -.mx_UserSettings_cryptoSection span { - display: table-cell; - padding-right: 1em; -} - -.mx_UserSettings_passwordWarning { - /* To move the "Sign out" button out of the way */ - clear: both; - color: $warning-color; - margin-bottom: 5px; -} - -.mx_UserSettings_importExportButtons { - padding-top: 10px; - padding-left: 40px; -} - -.mx_UserSettings_importExportButtons .mx_UserSettings_button { - margin-right: 1em; -} - -.mx_UserSettings_toggle input { - width: 16px; - margin-right: 8px; - margin-bottom: 8px; -} - -.mx_UserSettings_toggle label { - padding-bottom: 21px; -} - -.mx_UserSettings_accountTable -.mx_UserSettings_notifTable -{ - display: table; -} - -.mx_UserSettings_notifTable .mx_Spinner { - position: absolute; -} - -.mx_UserSettings_language { - width: 200px; -} - -.mx_UserSettings_webRtcDevices_dropdown { - width: 50%; -} - -.mx_UserSettings_profileTable -{ - display: table; - float: left; -} - -.mx_UserSettings_profileTableRow -{ - display: table-row; -} - -.mx_UserSettings_profileLabelCell -{ - padding-bottom: 21px; - display: table-cell; - font-weight: bold; - padding-right: 24px; -} - -.mx_UserSettings_profileInputCell { - display: table-cell; - padding-bottom: 21px; - width: 240px; -} - -.mx_UserSettings_profileInputCell input, -.mx_UserSettings_profileInputCell .mx_EditableText -{ - display: inline-block; - border: 0px; - border-bottom: 1px solid $input-underline-color; - padding: 0px; - width: 240px; - color: $input-fg-color; - font-family: $font-family; - font-size: 16px; -} - -.mx_UserSettings_threepidButton { - display: table-cell; - padding-left: 0.5em; - position: relative; - cursor: pointer; -} - -.mx_UserSettings_phoneSection { - display:table; -} - -.mx_UserSettings_phoneCountry { - width: 70px; - display: table-cell; -} - -input.mx_UserSettings_phoneNumberField { - margin-left: 3px; - width: 172px; - border: 1px solid transparent; -} - -.mx_UserSettings_changePasswordButton { - float: right; - margin-right: 32px; - margin-left: 32px; -} - -.mx_UserSettings_logout { - float: right; - margin-right: 32px; - margin-left: 32px; -} - -.mx_UserSettings_avatarPicker { - margin-left: 32px; - margin-right: 32px; - float: right; - cursor: pointer; -} - -.mx_UserSettings_avatarPicker_img .mx_BaseAvatar_image { - object-fit: cover; -} - -.mx_UserSettings_avatarPicker_edit { - text-align: center; - margin-top: 10px; -} - -.mx_UserSettings_avatarPicker_edit img { - cursor: pointer; -} - -.mx_UserSettings_avatarPicker_edit > input { - display: none; -} - -.mx_UserSettings_avatarPicker_imgContainer { - display: inline-block; -} - -.mx_UserSettings_avatarPicker_remove { - display: inline-block; - float: right; - margin-right: -15px; -} - -.mx_UserSettings_advanced_spoiler, -.mx_UserSettings_link { - cursor: pointer; - color: $accent-color; - word-break: break-all; -} - -.mx_UserSettings_analyticsModal table { - margin: 10px 0px; -} - - -// Temp styles to keep the layout moderately usable. Not perfect, but better -// than 30 options being impossible to understand. -.mx_UserSettings .mx_SettingsFlag { - height: 30px; -} - -.mx_UserSettings .mx_SettingsFlag .mx_ToggleSwitch { - float: left; - margin-right: 5px; -} \ No newline at end of file diff --git a/res/css/structures/_ViewSource.scss b/res/css/structures/_ViewSource.scss index a4c7dcf58a..b908861c6f 100644 --- a/res/css/structures/_ViewSource.scss +++ b/res/css/structures/_ViewSource.scss @@ -14,6 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_ViewSource_label_left { + float: left; +} + +.mx_ViewSource_label_right { + float: right; +} + +.mx_ViewSource_label_bottom { + clear: both; + border-bottom: 1px solid #e5e5e5; +} + .mx_ViewSource pre { text-align: left; font-size: 12px; diff --git a/res/css/structures/auth/_Login.scss b/res/css/structures/auth/_Login.scss index b48021c27a..4eff5c33e4 100644 --- a/res/css/structures/auth/_Login.scss +++ b/res/css/structures/auth/_Login.scss @@ -15,17 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_Login_field { - width: 100%; - box-sizing: border-box; - border-radius: 3px; - border: 1px solid $strong-input-border-color; - font-weight: 300; - font-size: 13px; - padding: 9px; - margin-bottom: 14px; -} - .mx_Login_submit { @mixin mx_DialogButton; width: 100%; @@ -69,74 +58,28 @@ limitations under the License. color: $warning-color; font-weight: bold; text-align: center; -/* - height: 24px; -*/ margin-top: 12px; margin-bottom: 12px; } .mx_Login_type_container { display: flex; - margin-bottom: 14px; - color: $primary-fg-color; + align-items: center; + color: $authpage-primary-color; + + .mx_Field { + margin: 0; + } } .mx_Login_type_label { flex-grow: 1; - line-height: 35px; } .mx_Login_type_dropdown { - display: inline-block; - min-width: 170px; - align-self: flex-end; - flex: 1 1 auto; + min-width: 200px; } -.mx_Login_field_prefix { - height: 38px; - padding: 0px 5px; - line-height: 38px; - - background-color: #eee; - border: 1px solid #c7c7c7; - border-right: 0px; - border-radius: 3px 0px 0px 3px; - - text-align: center; -} - -.mx_Login_field_has_prefix { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; -} - -.mx_Login_phoneSection { - display:flex; -} - -.mx_Login_phoneCountry { - margin-bottom: 14px; - - /* To override mx_Login_field_prefix */ - text-align: left; - padding: 0px; - background-color: $primary-bg-color; -} - -.mx_Login_field_prefix .mx_Dropdown_input { - /* To use prefix border instead of dropdown border */ - border: 0; -} - -.mx_Login_phoneCountry .mx_Dropdown_option { - /* To match height of mx_Login_field */ - height: 38px; - line-height: 38px; -} - -.mx_Login_phoneCountry .mx_Dropdown_option img { - margin: 3px; - vertical-align: top; +.mx_Login_underlinedServerName { + border-bottom: 1px dashed $accent-color; } diff --git a/res/css/views/auth/_AuthBody.scss b/res/css/views/auth/_AuthBody.scss index fd54cd135a..16ac876869 100644 --- a/res/css/views/auth/_AuthBody.scss +++ b/res/css/views/auth/_AuthBody.scss @@ -21,19 +21,80 @@ limitations under the License. padding: 25px 60px; box-sizing: border-box; font-size: 12px; - color: $authpage-body-color; -} + color: $authpage-secondary-color; -.mx_AuthBody h2 { - font-size: 24px; - font-weight: 600; - margin-top: 8px; -} + h2 { + font-size: 24px; + font-weight: 600; + margin-top: 8px; + color: $authpage-primary-color; + } -.mx_AuthBody h3 { - font-size: 14px; - font-weight: 600; - color: $primary-fg-color; + h3 { + font-size: 14px; + font-weight: 600; + color: $authpage-primary-color; + } + + a:link, + a:hover, + a:visited { + color: $accent-color; + text-decoration: none; + } + + input[type=text], + input[type=password] { + color: $authpage-primary-color; + } + + .mx_Field input, + .mx_Field select { + color: $authpage-primary-color; + background-color: $authpage-body-bg-color; + } + + .mx_Field label { + color: $authpage-primary-color; + } + + .mx_Field input:focus + label, + .mx_Field input:not(:placeholder-shown) + label, + .mx_Field textarea:focus + label, + .mx_Field textarea:not(:placeholder-shown) + label, + .mx_Field select + label /* Always show a select's label on top to not collide with the value */, + .mx_Field_labelAlwaysTopLeft label { + background-color: $authpage-body-bg-color; + } + + input.error { + color: $warning-color; + } + + .mx_Field input { + width: 100%; + box-sizing: border-box; + } + + .mx_Field_select::before { + background-color: $authpage-primary-color; + } + + .mx_Dropdown { + color: $authpage-primary-color; + } + + .mx_Dropdown_arrow { + background: $authpage-primary-color; + } + + .mx_Dropdown_menu { + background-color: $authpage-body-bg-color; + + .mx_Dropdown_option_highlight { + background-color: $authpage-focus-bg-color; + } + } } .mx_AuthBody_editServerDetails { @@ -42,38 +103,54 @@ limitations under the License. font-weight: normal; } -.mx_AuthBody .mx_Field input { - width: 100%; - box-sizing: border-box; -} - .mx_AuthBody_fieldRow { display: flex; margin-bottom: 10px; } -.mx_AuthBody_fieldRow > * { +.mx_AuthBody_fieldRow > .mx_Field { margin: 0 5px; flex: 1; } -.mx_AuthBody_fieldRow > *:first-child { +.mx_AuthBody_fieldRow > .mx_Field:first-child { margin-left: 0; } -.mx_AuthBody_fieldRow > *:last-child { +.mx_AuthBody_fieldRow > .mx_Field:last-child { margin-right: 0; } -.mx_AuthBody a:link, -.mx_AuthBody a:hover, -.mx_AuthBody a:visited { - color: $accent-color; - text-decoration: none; -} - .mx_AuthBody_changeFlow { display: block; text-align: center; width: 100%; } + +.mx_AuthBody_spinner { + margin: 1em 0; +} + +.mx_AuthBody_passwordScore { + width: 100%; + appearance: none; + height: 4px; + border: 0; + border-radius: 2px; + position: absolute; + top: -12px; + + &::-moz-progress-bar { + border-radius: 2px; + background-color: $accent-color; + } + + &::-webkit-progress-bar, + &::-webkit-progress-value { + border-radius: 2px; + } + + &::-webkit-progress-value { + background-color: $accent-color; + } +} diff --git a/res/css/views/auth/_ServerConfig.scss b/res/css/views/auth/_ServerConfig.scss index 79ad9e8238..fe96da2019 100644 --- a/res/css/views/auth/_ServerConfig.scss +++ b/res/css/views/auth/_ServerConfig.scss @@ -35,3 +35,8 @@ limitations under the License. .mx_ServerConfig_help:link { opacity: 0.8; } + +.mx_ServerConfig_error { + display: block; + color: $warning-color; +} diff --git a/res/css/views/auth/_ServerTypeSelector.scss b/res/css/views/auth/_ServerTypeSelector.scss index 03f5386501..ed781726b7 100644 --- a/res/css/views/auth/_ServerTypeSelector.scss +++ b/res/css/views/auth/_ServerTypeSelector.scss @@ -34,7 +34,7 @@ limitations under the License. .mx_ServerTypeSelector_label { text-align: center; font-weight: 600; - color: $primary-fg-color; + color: $authpage-primary-color; margin: 8px 0; } @@ -54,7 +54,7 @@ limitations under the License. height: 18px; margin-bottom: 12px; font-weight: 600; - color: $primary-fg-color; + color: $authpage-primary-color; } .mx_ServerTypeSelector_logo > div { diff --git a/res/css/views/auth/_Welcome.scss b/res/css/views/auth/_Welcome.scss new file mode 100644 index 0000000000..9043289184 --- /dev/null +++ b/res/css/views/auth/_Welcome.scss @@ -0,0 +1,26 @@ +/* +Copyright 2019 New Vector Ltd + +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_Welcome { + display: flex; + flex-direction: column; + align-items: center; +} + +.mx_Welcome .mx_AuthBody_language { + width: 120px; + margin-bottom: 10px; +} diff --git a/res/css/views/avatars/_BaseAvatar.scss b/res/css/views/avatars/_BaseAvatar.scss index ee2d9c190f..a085034758 100644 --- a/res/css/views/avatars/_BaseAvatar.scss +++ b/res/css/views/avatars/_BaseAvatar.scss @@ -16,6 +16,17 @@ limitations under the License. .mx_BaseAvatar { position: relative; + // In at least Firefox, the case of relative positioned inline elements + // (such as mx_BaseAvatar) with absolute positioned children (such as + // mx_BaseAvatar_initial) is a dark corner full of spider webs. It will give + // different results during full reflow of the page vs. incremental reflow + // of small portions. While that's surely a browser bug, we can avoid it by + // using `inline-block` instead of the default `inline`. + // https://github.com/vector-im/riot-web/issues/5594 + // https://bugzilla.mozilla.org/show_bug.cgi?id=1535053 + // https://bugzilla.mozilla.org/show_bug.cgi?id=255139 + display: inline-block; + user-select: none; } .mx_BaseAvatar_initial { diff --git a/res/css/views/context_menus/_TopLeftMenu.scss b/res/css/views/context_menus/_TopLeftMenu.scss index 18463da824..113da004b8 100644 --- a/res/css/views/context_menus/_TopLeftMenu.scss +++ b/res/css/views/context_menus/_TopLeftMenu.scss @@ -15,24 +15,54 @@ limitations under the License. */ .mx_TopLeftMenu { - min-width: 180px; + min-width: 210px; border-radius: 4px; + .mx_TopLeftMenu_greyedText { + font-size: 12px; + opacity: 0.5; + } + + .mx_TopLeftMenu_upgradeLink { + font-size: 12px; + + img { + margin-left: 5px; + } + } + .mx_TopLeftMenu_section:not(:last-child) { border-bottom: 1px solid $menu-border-color; } - .mx_TopLeftMenu_section { - list-style: none; + .mx_TopLeftMenu_section_noIcon { + margin: 5px 0; + padding: 5px 20px 5px 15px; + + div:not(:first-child) { + margin-top: 5px; + } + } + + .mx_TopLeftMenu_section_withIcon { margin: 5px 0; padding: 0; + list-style: none; + + li.mx_TopLeftMenu_icon_home::after { + mask-image: url('$(res)/img/feather-customised/home.svg'); + } li.mx_TopLeftMenu_icon_settings::after { - mask-image: url('$(res)/img/feather-icons/settings.svg'); + mask-image: url('$(res)/img/feather-customised/settings.svg'); + } + + li.mx_TopLeftMenu_icon_signin::after { + mask-image: url('$(res)/img/feather-customised/sign-in.svg'); } li.mx_TopLeftMenu_icon_signout::after { - mask-image: url('$(res)/img/feather-icons/sign-out.svg'); + mask-image: url('$(res)/img/feather-customised/sign-out.svg'); } li::after { diff --git a/res/css/views/dialogs/_ChatInviteDialog.scss b/res/css/views/dialogs/_AddressPickerDialog.scss similarity index 83% rename from res/css/views/dialogs/_ChatInviteDialog.scss rename to res/css/views/dialogs/_AddressPickerDialog.scss index dcc0f5921a..b4d4a74cb5 100644 --- a/res/css/views/dialogs/_ChatInviteDialog.scss +++ b/res/css/views/dialogs/_AddressPickerDialog.scss @@ -1,5 +1,6 @@ /* Copyright 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +16,8 @@ limitations under the License. */ /* Using a textarea for this element, to circumvent autofill */ -.mx_ChatInviteDialog_input, -.mx_ChatInviteDialog_input:focus +.mx_AddressPickerDialog_input, +.mx_AddressPickerDialog_input:focus { height: 26px; font-size: 14px; @@ -34,11 +35,11 @@ limitations under the License. word-wrap: nowrap; } -.mx_ChatInviteDialog .mx_Dialog_content { +.mx_AddressPickerDialog .mx_Dialog_content { min-height: 50px } -.mx_ChatInviteDialog_inputContainer { +.mx_AddressPickerDialog_inputContainer { border-radius: 3px; border: solid 1px $input-border-color; line-height: 36px; @@ -51,19 +52,19 @@ limitations under the License. overflow-y: auto; } -.mx_ChatInviteDialog_error { +.mx_AddressPickerDialog_error { margin-top: 10px; color: $warning-color; } -.mx_ChatInviteDialog_cancel { +.mx_AddressPickerDialog_cancel { position: absolute; right: 11px; top: 13px; cursor: pointer; } -.mx_ChatInviteDialog_cancel object { +.mx_AddressPickerDialog_cancel object { pointer-events: none; } diff --git a/res/css/views/dialogs/_Analytics.scss b/res/css/views/dialogs/_Analytics.scss new file mode 100644 index 0000000000..e403d3b207 --- /dev/null +++ b/res/css/views/dialogs/_Analytics.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 New Vector Ltd. + +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_AnalyticsModal table { + margin: 10px 0px; +} diff --git a/res/css/views/dialogs/_BugReportDialog.scss b/res/css/views/dialogs/_BugReportDialog.scss index e00d446eda..90ef55b945 100644 --- a/res/css/views/dialogs/_BugReportDialog.scss +++ b/res/css/views/dialogs/_BugReportDialog.scss @@ -14,39 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_BugReportDialog_field_container { - display: flex; -} - -.mx_BugReportDialog_field_label { - flex-basis: 150px; - - text-align: right; - - padding-top: 9px; - padding-right: 4px; - - line-height: 18px; +.mx_BugReportDialog .mx_Field { + flex: 1; } .mx_BugReportDialog_field_input { - flex-grow: 1; - - /* taken from mx_ChatInviteDialog_inputContainer */ - border-radius: 3px; - border: solid 1px $input-border-color; - - font-size: 14px; - - padding-left: 4px; - padding-right: 4px; - padding-top: 7px; - padding-bottom: 7px; - - margin-bottom: 4px; -} - -.mx_BugReportDialog_field_input[type="text" i] { - padding-top: 9px; - padding-bottom: 9px; + // TODO: We should really apply this to all .mx_Field inputs. + // See https://github.com/vector-im/riot-web/issues/9344. + flex: 1; } diff --git a/res/css/views/dialogs/_CreateGroupDialog.scss b/res/css/views/dialogs/_CreateGroupDialog.scss index 500e12ee49..128eacc3ce 100644 --- a/res/css/views/dialogs/_CreateGroupDialog.scss +++ b/res/css/views/dialogs/_CreateGroupDialog.scss @@ -43,10 +43,9 @@ limitations under the License. .mx_CreateGroupDialog_prefix, .mx_CreateGroupDialog_suffix { - height: 35px; padding: 0px 5px; line-height: 37px; - background-color: $input-border-color; + background-color: $input-darker-bg-color; border: 1px solid $input-border-color; text-align: center; } diff --git a/res/css/views/elements/_HexVerify.scss b/res/css/views/dialogs/_DeviceVerifyDialog.scss similarity index 67% rename from res/css/views/elements/_HexVerify.scss rename to res/css/views/dialogs/_DeviceVerifyDialog.scss index 3f3ee4b7ea..1997e0c21d 100644 --- a/res/css/views/elements/_HexVerify.scss +++ b/res/css/views/dialogs/_DeviceVerifyDialog.scss @@ -14,21 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_HexVerify { - text-align: center; +.mx_DeviceVerifyDialog_cryptoSection ul { + display: table; } -.mx_HexVerify_pair { - display: inline-block; - font-weight: bold; - padding-left: 3px; - padding-right: 3px; +.mx_DeviceVerifyDialog_cryptoSection li { + display: table-row; } -.mx_HexVerify_pair_verified { - color: $accent-color; -} - -.mx_HexVerify_pair:hover{ - color: $accent-color; +.mx_DeviceVerifyDialog_cryptoSection label, +.mx_DeviceVerifyDialog_cryptoSection span { + display: table-cell; + padding-right: 1em; } diff --git a/res/css/views/dialogs/_DevtoolsDialog.scss b/res/css/views/dialogs/_DevtoolsDialog.scss index 572d6ee8c7..1f5d36b57a 100644 --- a/res/css/views/dialogs/_DevtoolsDialog.scss +++ b/res/css/views/dialogs/_DevtoolsDialog.scss @@ -18,7 +18,12 @@ limitations under the License. margin: 10px 0; } -.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_RoomStateExplorer_query { +.mx_DevTools_ServersInRoomList_button { + /* Set the cursor back to default as `.mx_Dialog button` sets it to pointer */ + cursor: default !important; +} + +.mx_DevTools_RoomStateExplorer_button, .mx_DevTools_ServersInRoomList_button, .mx_DevTools_RoomStateExplorer_query { margin-bottom: 10px; width: 100%; } @@ -67,12 +72,25 @@ limitations under the License. .mx_DevTools_textarea { font-size: 12px; - max-width: 624px; + max-width: 684px; min-height: 250px; padding: 10px; width: 100%; } +.mx_DevTools_content .mx_Field_input { + display: inline-block; +} + +.mx_DevTools_eventTypeStateKeyGroup { + display: flex; + flex-wrap: wrap; +} + +.mx_DevTools_content .mx_Field_input:first-of-type { + margin-right: 42px; +} + .mx_DevTools_tgl { display: none; diff --git a/res/css/views/dialogs/_IncomingSasDialog.scss b/res/css/views/dialogs/_IncomingSasDialog.scss new file mode 100644 index 0000000000..3a9d645a98 --- /dev/null +++ b/res/css/views/dialogs/_IncomingSasDialog.scss @@ -0,0 +1,24 @@ +/* +Copyright 2019 New Vector Ltd. + +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_IncomingSasDialog_opponentProfile_image { + position: relative; +} + +.mx_IncomingSasDialog_opponentProfile h2 { + display: inline-block; + margin-left: 10px; +} diff --git a/res/css/views/dialogs/_RoomSettingsDialog.scss b/res/css/views/dialogs/_RoomSettingsDialog.scss index de675abe32..60b35528a1 100644 --- a/res/css/views/dialogs/_RoomSettingsDialog.scss +++ b/res/css/views/dialogs/_RoomSettingsDialog.scss @@ -18,17 +18,27 @@ limitations under the License. // ========================================================== .mx_RoomSettingsDialog_settingsIcon:before { - mask-image: url('$(res)/img/feather-icons/settings.svg'); + mask-image: url('$(res)/img/feather-customised/settings.svg'); } .mx_RoomSettingsDialog_securityIcon:before { - mask-image: url('$(res)/img/feather-icons/lock.svg'); + mask-image: url('$(res)/img/feather-customised/lock.svg'); } .mx_RoomSettingsDialog_rolesIcon:before { - mask-image: url('$(res)/img/feather-icons/users-sm.svg'); + mask-image: url('$(res)/img/feather-customised/users-sm.svg'); } .mx_RoomSettingsDialog_warningIcon:before { - mask-image: url('$(res)/img/feather-icons/warning-triangle.svg'); + mask-image: url('$(res)/img/feather-customised/warning-triangle.svg'); +} + +.mx_RoomSettingsDialog .mx_Dialog_title { + -ms-text-overflow: ellipsis; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin: 0 auto; + padding-left: 40px; + padding-right: 80px; } diff --git a/res/css/views/dialogs/_SettingsDialog.scss b/res/css/views/dialogs/_SettingsDialog.scss index 4a9708f6d1..ec813a1a07 100644 --- a/res/css/views/dialogs/_SettingsDialog.scss +++ b/res/css/views/dialogs/_SettingsDialog.scss @@ -14,34 +14,29 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_SettingsDialog { - .mx_Dialog { - max-width: 900px; - width: 80%; - height: 80%; - border-radius: 4px; - padding-top: 0; - padding-right: 0; - padding-left: 0; +// Not actually a component but things shared by settings components +.mx_UserSettingsDialog, .mx_RoomSettingsDialog { + width: 90vw; + max-width: 1000px; + // set the height too since tabbed view scrolls itself. + height: 80vh; - .mx_TabbedView { - top: 65px; - } + .mx_TabbedView { + top: 65px; + } - .mx_TabbedView .mx_SettingsTab { - box-sizing: border-box; - min-width: 550px; - padding-right: 130px; + .mx_TabbedView .mx_SettingsTab { + box-sizing: border-box; + min-width: 580px; + padding-right: 100px; - // Put some padding on the bottom to avoid the settings tab from - // colliding harshly with the dialog when scrolled down. - padding-bottom: 100px; - } + // Put some padding on the bottom to avoid the settings tab from + // colliding harshly with the dialog when scrolled down. + padding-bottom: 100px; + } - .mx_Dialog_title { - text-align: center; - margin-top: 16px; - margin-bottom: 24px; - } + .mx_Dialog_title { + text-align: center; + margin-bottom: 24px; } } diff --git a/res/css/views/dialogs/_UnknownDeviceDialog.scss b/res/css/views/dialogs/_UnknownDeviceDialog.scss index a924704769..02e0fb1fe5 100644 --- a/res/css/views/dialogs/_UnknownDeviceDialog.scss +++ b/res/css/views/dialogs/_UnknownDeviceDialog.scss @@ -28,28 +28,28 @@ limitations under the License. flex-direction: column; } +.mx_UnknownDeviceDialog ul { + list-style: none; + padding: 0; +} +// userid +.mx_UnknownDeviceDialog p { + font-weight: bold; + font-size: 16px; +} + .mx_UnknownDeviceDialog .mx_DeviceVerifyButtons { - float: right; + flex-direction: row !important; } .mx_UnknownDeviceDialog .mx_Dialog_content { margin-bottom: 24px; } -.mx_UnknownDeviceDialog .mx_MemberDeviceInfo { - float: right; - clear: both; - padding: 0px; - padding-top: 8px; +.mx_UnknownDeviceDialog_deviceList > li { + padding: 4px; } -.mx_UnknownDeviceDialog .mx_MemberDeviceInfo_textButton { - @mixin mx_DialogButton_small; - background-color: $primary-bg-color; - color: $accent-color; -} - -.mx_UnknownDeviceDialog .mx_UnknownDeviceDialog_deviceList li { - height: 40px; - border-bottom: 1px solid $primary-hairline-color; +.mx_UnknownDeviceDialog_deviceList > li > * { + padding-bottom: 0; } diff --git a/res/css/views/dialogs/_UploadConfirmDialog.scss b/res/css/views/dialogs/_UploadConfirmDialog.scss new file mode 100644 index 0000000000..cf9736ce81 --- /dev/null +++ b/res/css/views/dialogs/_UploadConfirmDialog.scss @@ -0,0 +1,35 @@ +/* +Copyright 2019 New Vector Ltd. + +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_UploadConfirmDialog_fileIcon { + margin-right: 5px; +} + +.mx_UploadConfirmDialog_previewOuter { + text-align: center; +} + +.mx_UploadConfirmDialog_previewInner { + display: inline-block; + text-align: left; +} + +.mx_UploadConfirmDialog_imagePreview { + max-height: 300px; + max-width: 100%; + border-radius: 4px; + border: 1px solid $dialog-close-fg-color; +} diff --git a/res/css/views/dialogs/_UserSettingsDialog.scss b/res/css/views/dialogs/_UserSettingsDialog.scss index 814a738b5f..9665ee06b4 100644 --- a/res/css/views/dialogs/_UserSettingsDialog.scss +++ b/res/css/views/dialogs/_UserSettingsDialog.scss @@ -18,33 +18,33 @@ limitations under the License. // ========================================================== .mx_UserSettingsDialog_settingsIcon:before { - mask-image: url('$(res)/img/feather-icons/settings.svg'); + mask-image: url('$(res)/img/feather-customised/settings.svg'); } .mx_UserSettingsDialog_voiceIcon:before { - mask-image: url('$(res)/img/feather-icons/phone.svg'); + mask-image: url('$(res)/img/feather-customised/phone.svg'); } .mx_UserSettingsDialog_bellIcon:before { - mask-image: url('$(res)/img/feather-icons/notifications.svg'); + mask-image: url('$(res)/img/feather-customised/notifications.svg'); } .mx_UserSettingsDialog_preferencesIcon:before { - mask-image: url('$(res)/img/feather-icons/sliders.svg'); + mask-image: url('$(res)/img/feather-customised/sliders.svg'); } .mx_UserSettingsDialog_securityIcon:before { - mask-image: url('$(res)/img/feather-icons/lock.svg'); + mask-image: url('$(res)/img/feather-customised/lock.svg'); } .mx_UserSettingsDialog_helpIcon:before { - mask-image: url('$(res)/img/feather-icons/help-circle.svg'); + mask-image: url('$(res)/img/feather-customised/help-circle.svg'); } .mx_UserSettingsDialog_labsIcon:before { - mask-image: url('$(res)/img/feather-icons/flag.svg'); + mask-image: url('$(res)/img/feather-customised/flag.svg'); } .mx_UserSettingsDialog_flairIcon:before { - mask-image: url('$(res)/img/feather-icons/flair.svg'); + mask-image: url('$(res)/img/feather-customised/flair.svg'); } diff --git a/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss b/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss new file mode 100644 index 0000000000..a419c105a9 --- /dev/null +++ b/res/css/views/dialogs/_WidgetOpenIDPermissionsDialog.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 Travis Ralston + +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_WidgetOpenIDPermissionsDialog .mx_SettingsFlag { + .mx_ToggleSwitch { + display: inline-block; + vertical-align: middle; + margin-right: 8px; + } + + .mx_SettingsFlag_label { + display: inline-block; + vertical-align: middle; + } +} diff --git a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss index bd703badaa..f905516bd4 100644 --- a/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss +++ b/res/css/views/dialogs/keybackup/_KeyBackupFailedDialog.scss @@ -24,7 +24,7 @@ limitations under the License. padding-bottom: 10px; &:before { - mask: url("$(res)/img/e2e/lock-warning.svg"); + mask: url("$(res)/img/e2e/lock-warning-filled.svg"); mask-repeat: no-repeat; background-color: $primary-fg-color; content: ""; diff --git a/res/css/views/elements/_AccessibleButton.scss b/res/css/views/elements/_AccessibleButton.scss index 8de0b82554..fe1f283009 100644 --- a/res/css/views/elements/_AccessibleButton.scss +++ b/res/css/views/elements/_AccessibleButton.scss @@ -37,11 +37,16 @@ limitations under the License. .mx_AccessibleButton_kind_primary { color: $button-primary-fg-color; background-color: $button-primary-bg-color; + font-weight: 600; +} + +.mx_AccessibleButton_kind_secondary { + color: $accent-color; + font-weight: 600; } .mx_AccessibleButton_kind_primary.mx_AccessibleButton_disabled { - color: $button-primary-disabled-fg-color; - background-color: $button-primary-disabled-bg-color; + opacity: 0.4; } .mx_AccessibleButton_hasKind.mx_AccessibleButton_kind_primary_sm { @@ -51,8 +56,7 @@ limitations under the License. } .mx_AccessibleButton_kind_primary_sm.mx_AccessibleButton_disabled { - color: $button-primary-disabled-fg-color; - background-color: $button-primary-disabled-bg-color; + opacity: 0.4; } .mx_AccessibleButton_kind_danger { @@ -74,4 +78,4 @@ limitations under the License. .mx_AccessibleButton_kind_danger_sm.mx_AccessibleButton_disabled { color: $button-danger-disabled-fg-color; background-color: $button-danger-disabled-bg-color; -} \ No newline at end of file +} diff --git a/res/css/views/elements/_Dropdown.scss b/res/css/views/elements/_Dropdown.scss index 27b9975570..2a59393499 100644 --- a/res/css/views/elements/_Dropdown.scss +++ b/res/css/views/elements/_Dropdown.scss @@ -16,6 +16,7 @@ limitations under the License. .mx_Dropdown { position: relative; + color: $primary-fg-color; } .mx_Dropdown_disabled { @@ -33,7 +34,7 @@ limitations under the License. } .mx_Dropdown_input:focus { - border-color: $accent-color; + border-color: $input-focused-border-color; } /* Disable dropdown highlight on focus */ @@ -45,7 +46,7 @@ limitations under the License. width: 10px; height: 6px; padding-right: 9px; - mask: url('$(res)/img/feather-icons/dropdown-arrow.svg'); + mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; background: $primary-fg-color; } @@ -76,7 +77,9 @@ limitations under the License. vertical-align: middle; } -input.mx_Dropdown_option, input.mx_Dropdown_option:focus { +input.mx_Dropdown_option, +input.mx_Dropdown_option:focus { + font-weight: normal; border: 0; padding-top: 0; padding-bottom: 0; @@ -95,7 +98,7 @@ input.mx_Dropdown_option, input.mx_Dropdown_option:focus { margin: 0; padding: 0px; border-radius: 3px; - border: 1px solid $accent-color; + border: 1px solid $input-focused-border-color; background-color: $primary-bg-color; max-height: 200px; overflow-y: auto; @@ -110,10 +113,6 @@ input.mx_Dropdown_option, input.mx_Dropdown_option:focus { background-color: $focus-bg-color; } -.mx_Dropdown_menu { - font-weight: bold; -} - .mx_Dropdown_searchPrompt { font-weight: normal; margin-left: 5px; diff --git a/res/css/views/elements/_EditableItemList.scss b/res/css/views/elements/_EditableItemList.scss index 9fbb39aa17..be96d811d3 100644 --- a/res/css/views/elements/_EditableItemList.scss +++ b/res/css/views/elements/_EditableItemList.scss @@ -1,5 +1,5 @@ /* -Copyright 2017 New Vector Ltd. +Copyright 2017, 2019 New Vector Ltd. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,47 +16,38 @@ limitations under the License. .mx_EditableItemList { margin-top: 12px; - margin-bottom: 0px; + margin-bottom: 10px; } .mx_EditableItem { - display: flex; - margin-left: 56px; + margin-bottom: 5px; + margin-left: 15px; } -.mx_EditableItem .mx_EditableItem_editable { - border: 0px; - border-bottom: 1px solid $strong-input-border-color; - padding: 0px; - min-width: 240px; - max-width: 400px; - margin-bottom: 16px; -} - -.mx_EditableItem .mx_EditableItem_editable:focus { - border-bottom: 1px solid $accent-color; - outline: none; - box-shadow: none; -} - -.mx_EditableItem .mx_EditableItem_editablePlaceholder { - color: $settings-grey-fg-color; -} - -.mx_EditableItem .mx_EditableItem_addButton, -.mx_EditableItem .mx_EditableItem_removeButton { - padding-left: 0.5em; - position: relative; +.mx_EditableItem_delete { + margin-right: 5px; cursor: pointer; - - visibility: hidden; + vertical-align: middle; } -.mx_EditableItem:hover .mx_EditableItem_addButton, -.mx_EditableItem:hover .mx_EditableItem_removeButton { - visibility: visible; +.mx_EditableItem_email { + vertical-align: middle; +} + +.mx_EditableItem_promptText { + margin-right: 10px; +} + +.mx_EditableItem_confirmBtn { + margin-right: 5px; +} + +.mx_EditableItemList_newItem .mx_Field input { + // Use 100% of the space available for the input, but don't let the 10px + // padding on either side of the input to push it out of alignment. + width: calc(100% - 20px); } .mx_EditableItemList_label { - margin-bottom: 8px; -} + margin-bottom: 5px; +} \ No newline at end of file diff --git a/res/css/views/elements/_Field.scss b/res/css/views/elements/_Field.scss index 06002939f5..147bb3b471 100644 --- a/res/css/views/elements/_Field.scss +++ b/res/css/views/elements/_Field.scss @@ -14,11 +14,19 @@ See the License for the specific language governing permissions and limitations under the License. */ -/* TODO: Consider unifying with general input styles in _dharma.scss */ +/* TODO: Consider unifying with general input styles in _light.scss */ .mx_Field { + display: flex; position: relative; margin: 1em 0; + border-radius: 4px; + transition: border-color 0.25s; + border: 1px solid $input-border-color; +} + +.mx_Field_prefix { + border-right: 1px solid $input-border-color; } .mx_Field input, @@ -27,10 +35,12 @@ limitations under the License. font-weight: normal; font-family: $font-family; font-size: 14px; + border: none; + // Even without a border here, we still need this avoid overlapping the rounded + // corners on the field above. border-radius: 4px; - transition: border-color 0.25s; - border: 1px solid $input-border-color; padding: 8px 9px; + color: $primary-fg-color; background-color: $primary-bg-color; } @@ -47,18 +57,21 @@ limitations under the License. right: 10px; width: 10px; height: 6px; - mask: url('$(res)/img/feather-icons/dropdown-arrow.svg'); + mask: url('$(res)/img/feather-customised/dropdown-arrow.svg'); mask-repeat: no-repeat; background-color: $primary-fg-color; z-index: 1; pointer-events: none; } +.mx_Field:focus-within { + border-color: $input-focused-border-color; +} + .mx_Field input:focus, .mx_Field select:focus, .mx_Field textarea:focus { outline: 0; - border-color: $input-focused-border-color; } .mx_Field input::placeholder, @@ -87,21 +100,29 @@ limitations under the License. top: 0px; margin: 7px 8px; padding: 2px; + pointer-events: none; // Allow clicks to fall through to the input + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + max-width: calc(100% - 20px); // 100% of parent minus margin and padding } .mx_Field input:focus + label, .mx_Field input:not(:placeholder-shown) + label, .mx_Field textarea:focus + label, .mx_Field textarea:not(:placeholder-shown) + label, -.mx_Field select + label /* Always show a select's label on top to not collide with the value */ { +.mx_Field select + label /* Always show a select's label on top to not collide with the value */, +.mx_Field_labelAlwaysTopLeft label { transition: font-size 0.25s ease-out 0s, color 0.25s ease-out 0s, top 0.25s ease-out 0s, background-color 0.25s ease-out 0s; font-size: 10px; - top: -14px; + top: -13px; + padding: 0 2px; background-color: $field-focused-label-bg-color; + pointer-events: initial; } .mx_Field input:focus + label, @@ -111,7 +132,56 @@ limitations under the License. } .mx_Field select:disabled, -.mx_Field input:disabled { +.mx_Field select:disabled + label, +.mx_Field input:disabled, +.mx_Field input:disabled + label, +.mx_Field textarea:disabled, +.mx_Field textarea:disabled + label { background-color: $field-focused-label-bg-color; color: $greyed-fg-color; } + +.mx_Field_valid { + &.mx_Field, + &.mx_Field:focus-within { + border-color: $input-valid-border-color; + } + + &.mx_Field label, + &.mx_Field:focus-within label { + color: $input-valid-border-color; + } +} + +.mx_Field_invalid { + &.mx_Field, + &.mx_Field:focus-within { + border-color: $input-invalid-border-color; + } + + &.mx_Field label, + &.mx_Field:focus-within label { + color: $input-invalid-border-color; + } +} + +.mx_Field_tooltip { + margin-top: -12px; + margin-left: 4px; + width: 200px; +} + +.mx_Field_tooltip.mx_Field_valid { + animation: mx_fadeout 1s 2s forwards; +} + +// Customise other components when placed inside a Field + +.mx_Field .mx_Dropdown_input { + border: initial; + border-radius: initial; +} + +.mx_Field .mx_CountryDropdown { + width: 67px; +} diff --git a/res/css/views/elements/_ImageView.scss b/res/css/views/elements/_ImageView.scss index 9bf23b1025..88cf2ce8ba 100644 --- a/res/css/views/elements/_ImageView.scss +++ b/res/css/views/elements/_ImageView.scss @@ -77,9 +77,27 @@ limitations under the License. .mx_ImageView_cancel { position: absolute; - top: 0px; + // hack for mx_Dialog having a top padding of 40px + top: 40px; right: 0px; - padding: 35px; + padding-top: 35px; + padding-right: 35px; + cursor: pointer; +} + +.mx_ImageView_rotateClockwise { + position: absolute; + top: 40px; + right: 70px; + padding-top: 35px; + cursor: pointer; +} + +.mx_ImageView_rotateCounterClockwise { + position: absolute; + top: 40px; + right: 105px; + padding-top: 35px; cursor: pointer; } diff --git a/res/css/views/elements/_ManageIntegsButton.scss b/res/css/views/elements/_ManageIntegsButton.scss new file mode 100644 index 0000000000..7c91b9dbde --- /dev/null +++ b/res/css/views/elements/_ManageIntegsButton.scss @@ -0,0 +1,53 @@ +/* +Copyright 2019 New Vector Ltd. + +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_ManageIntegsButton_error { + position: relative; + cursor: not-allowed; +} + +.mx_ManageIntegsButton_error img { + position: absolute; + right: -5px; + top: -5px; +} + +.mx_ManageIntegsButton_error { + float: right; +} + +.mx_ManageIntegsButton_error .mx_ManageIntegsButton_errorPopup { + display: none; +} + +.mx_ManageIntegsButton_error:hover .mx_ManageIntegsButton_errorPopup { + display: inline; +} + +.mx_ManageIntegsButton_errorPopup { + position: absolute; + top: 110%; + left: -275%; + width: 550%; + padding: 30%; + font-size: 10pt; + line-height: 1.5em; + border-radius: 5px; + background-color: $accent-color; + color: $accent-fg-color; + text-align: center; + z-index: 1000; +} diff --git a/res/css/views/elements/_MessageEditor.scss b/res/css/views/elements/_MessageEditor.scss new file mode 100644 index 0000000000..e721b267fa --- /dev/null +++ b/res/css/views/elements/_MessageEditor.scss @@ -0,0 +1,90 @@ +/* +Copyright 2019 New Vector Ltd + +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_MessageEditor { + border-radius: 4px; + padding: 3px; + // this is to try not make the text move but still have some + // padding around and in the editor. + // Actual values from fiddling around in inspector + margin: -7px -10px -5px -10px; + overflow: visible !important; // override mx_EventTile_content + + .mx_MessageEditor_editor { + border-radius: 4px; + border: solid 1px $primary-hairline-color; + background-color: $primary-bg-color; + padding: 3px 6px; + white-space: pre-wrap; + word-wrap: break-word; + outline: none; + max-height: 200px; + overflow-x: auto; + + span.mx_UserPill, span.mx_RoomPill { + padding-left: 21px; + position: relative; + + // avatar psuedo element + &::before { + position: absolute; + left: 2px; + top: 2px; + content: var(--avatar-letter); + width: 16px; + height: 16px; + background: var(--avatar-background), $avatar-bg-color; + color: $avatar-initial-color; + background-repeat: no-repeat; + background-size: 16px; + border-radius: 8px; + text-align: center; + font-weight: normal; + line-height: 16px; + font-size: 10.4px; + } + } + } + + .mx_MessageEditor_buttons { + display: flex; + flex-direction: row; + justify-content: flex-end; + padding: 5px; + position: absolute; + left: 0; + background: $header-panel-bg-color; + z-index: 100; + right: 0; + margin: 0 -110px 0 0; + padding-right: 147px; + + .mx_AccessibleButton { + margin-left: 5px; + padding: 5px 40px; + } + } + + .mx_MessageEditor_AutoCompleteWrapper { + position: relative; + height: 0; + } +} + +.mx_EventTile_last .mx_MessageEditor_buttons { + position: static; + margin-right: -147px; +} diff --git a/res/css/views/elements/_PowerSelector.scss b/res/css/views/elements/_PowerSelector.scss new file mode 100644 index 0000000000..69f3a8eebb --- /dev/null +++ b/res/css/views/elements/_PowerSelector.scss @@ -0,0 +1,25 @@ +/* +Copyright 2019 New Vector Ltd. + +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_PowerSelector { + width: 100%; +} + +.mx_PowerSelector .mx_Field select, +.mx_PowerSelector .mx_Field input { + width: 100%; + box-sizing: border-box; +} diff --git a/res/css/views/elements/_TextWithTooltip.scss b/res/css/views/elements/_TextWithTooltip.scss new file mode 100644 index 0000000000..5b34e7a3be --- /dev/null +++ b/res/css/views/elements/_TextWithTooltip.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 New Vector Ltd. + +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_TextWithTooltip_tooltip { + display: none; +} \ No newline at end of file diff --git a/res/css/views/elements/_Tooltip.scss b/res/css/views/elements/_Tooltip.scss new file mode 100644 index 0000000000..3a6b6fb936 --- /dev/null +++ b/res/css/views/elements/_Tooltip.scss @@ -0,0 +1,92 @@ +/* +Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd + +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. +*/ + +@keyframes mx_fadein { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes mx_fadeout { + from { opacity: 1; } + to { opacity: 0; } +} + +.mx_Tooltip_chevron { + position: absolute; + left: -7px; + top: 10px; + width: 0; + height: 0; + border-top: 7px solid transparent; + border-right: 7px solid $menu-border-color; + border-bottom: 7px solid transparent; +} + +.mx_Tooltip_chevron:after { + content:''; + width: 0; + height: 0; + border-top: 6px solid transparent; + border-right: 6px solid $menu-bg-color; + border-bottom: 6px solid transparent; + position: absolute; + top: -6px; + left: 1px; +} + +.mx_Tooltip { + display: none; + position: fixed; + border: 1px solid $menu-border-color; + border-radius: 4px; + box-shadow: 4px 4px 12px 0 $menu-box-shadow-color; + background-color: $menu-bg-color; + z-index: 2000; + padding: 10px; + pointer-events: none; + line-height: 14px; + font-size: 12px; + font-weight: 600; + color: $primary-fg-color; + max-width: 200px; + word-break: break-word; + margin-right: 50px; + + &.mx_Tooltip_visible { + animation: mx_fadein 0.2s forwards; + } + + &.mx_Tooltip_invisible { + animation: mx_fadeout 0.1s forwards; + } +} + +.mx_Tooltip_timeline { + box-shadow: none; + background-color: $tooltip-timeline-bg-color; + color: $tooltip-timeline-fg-color; + text-align: center; + border: none; + border-radius: 3px; + font-size: 14px; + line-height: 1.2; + padding: 6px 8px; + + .mx_Tooltip_chevron::after { + border-right-color: $tooltip-timeline-bg-color; + } +} diff --git a/res/css/views/elements/_Validation.scss b/res/css/views/elements/_Validation.scss new file mode 100644 index 0000000000..1f9bd880e6 --- /dev/null +++ b/res/css/views/elements/_Validation.scss @@ -0,0 +1,69 @@ +/* +Copyright 2019 New Vector Ltd + +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_Validation { + position: relative; +} + +.mx_Validation_details { + padding-left: 20px; + margin: 0; +} + +.mx_Validation_description + .mx_Validation_details { + margin: 1em 0 0; +} + +.mx_Validation_detail { + position: relative; + font-weight: normal; + list-style: none; + margin-bottom: 0.5em; + + &:last-child { + margin-bottom: 0; + } + + &::before { + content: ""; + position: absolute; + width: 14px; + height: 14px; + top: 0; + left: -18px; + mask-repeat: no-repeat; + mask-position: center; + mask-size: contain; + } + + &.mx_Validation_valid { + color: $input-valid-border-color; + + &::before { + mask-image: url('$(res)/img/feather-customised/check.svg'); + background-color: $input-valid-border-color; + } + } + + &.mx_Validation_invalid { + color: $input-invalid-border-color; + + &::before { + mask-image: url('$(res)/img/feather-customised/x.svg'); + background-color: $input-invalid-border-color; + } + } +} diff --git a/res/css/views/messages/_CreateEvent.scss b/res/css/views/messages/_CreateEvent.scss index c095fc26af..adf16d6c4a 100644 --- a/res/css/views/messages/_CreateEvent.scss +++ b/res/css/views/messages/_CreateEvent.scss @@ -24,9 +24,14 @@ limitations under the License. .mx_CreateEvent_image { float: left; - padding-right: 20px; + margin-right: 20px; width: 72px; height: 34px; + + background-color: $primary-fg-color; + mask: url('$(res)/img/room-continuation.svg'); + mask-repeat: no-repeat; + mask-position: center; } .mx_CreateEvent_header { diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss new file mode 100644 index 0000000000..749cfeebe6 --- /dev/null +++ b/res/css/views/messages/_MessageActionBar.scss @@ -0,0 +1,78 @@ +/* +Copyright 2019 New Vector Ltd + +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_MessageActionBar { + position: absolute; + visibility: hidden; + cursor: pointer; + display: flex; + height: 24px; + line-height: 24px; + border-radius: 4px; + background: $message-action-bar-bg-color; + top: -18px; + right: 8px; + user-select: none; + + > * { + display: inline-block; + position: relative; + width: 27px; + border: 1px solid $message-action-bar-border-color; + margin-left: -1px; + + &:hover { + border-color: $message-action-bar-hover-border-color; + 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::after { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + mask-repeat: no-repeat; + mask-position: center; + background-color: $message-action-bar-fg-color; +} + +.mx_MessageActionBar_replyButton::after { + mask-image: url('$(res)/img/reply.svg'); +} + +.mx_MessageActionBar_editButton::after { + mask-image: url('$(res)/img/edit.svg'); +} + +.mx_MessageActionBar_optionsButton::after { + mask-image: url('$(res)/img/icon_context.svg'); +} diff --git a/res/css/views/messages/_ReactionDimension.scss b/res/css/views/messages/_ReactionDimension.scss new file mode 100644 index 0000000000..9a891d05cf --- /dev/null +++ b/res/css/views/messages/_ReactionDimension.scss @@ -0,0 +1,25 @@ +/* +Copyright 2019 New Vector Ltd + +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_ReactionDimension { + width: 42px; + display: flex; + justify-content: space-evenly; +} + +.mx_ReactionDimension_disabled { + opacity: 0.4; +} diff --git a/res/css/views/messages/_ReactionsRow.scss b/res/css/views/messages/_ReactionsRow.scss new file mode 100644 index 0000000000..fb66ffbb8c --- /dev/null +++ b/res/css/views/messages/_ReactionsRow.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 New Vector Ltd + +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_ReactionsRow { + margin: 6px 0; +} diff --git a/res/css/views/messages/_ReactionsRowButton.scss b/res/css/views/messages/_ReactionsRowButton.scss new file mode 100644 index 0000000000..3c6d019b30 --- /dev/null +++ b/res/css/views/messages/_ReactionsRowButton.scss @@ -0,0 +1,37 @@ +/* +Copyright 2019 New Vector Ltd + +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_ReactionsRowButton { + display: inline-block; + height: 20px; + line-height: 21px; + margin-right: 6px; + padding: 0 6px; + border: 1px solid $reaction-row-button-border-color; + border-radius: 10px; + background-color: $reaction-row-button-bg-color; + cursor: pointer; + user-select: none; + + &:hover { + border-color: $reaction-row-button-hover-border-color; + } + + &.mx_ReactionsRowButton_selected { + background-color: $reaction-row-button-selected-bg-color; + border-color: $reaction-row-button-selected-border-color; + } +} diff --git a/res/css/views/messages/_ReactionsRowButtonTooltip.scss b/res/css/views/messages/_ReactionsRowButtonTooltip.scss new file mode 100644 index 0000000000..cf4219fcec --- /dev/null +++ b/res/css/views/messages/_ReactionsRowButtonTooltip.scss @@ -0,0 +1,19 @@ +/* +Copyright 2019 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_ReactionsRowButtonTooltip_reactedWith { + opacity: 0.7; +} diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index a4a2aba11f..655cb39489 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -18,36 +18,3 @@ limitations under the License. font-weight: 600; } -.mx_SenderProfile_color1 { - color: $username-variant1-color; -} - -.mx_SenderProfile_color2 { - color: $username-variant2-color; -} - -.mx_SenderProfile_color3 { - color: $username-variant3-color; -} - -.mx_SenderProfile_color4 { - color: $username-variant4-color; -} - -.mx_SenderProfile_color5 { - color: $username-variant5-color; -} - -.mx_SenderProfile_color6 { - color: $username-variant6-color; -} - -.mx_SenderProfile_color7 { - color: $username-variant7-color; -} - -.mx_SenderProfile_color8 { - color: $username-variant8-color; -} - - diff --git a/res/css/views/messages/_ViewSourceEvent.scss b/res/css/views/messages/_ViewSourceEvent.scss new file mode 100644 index 0000000000..a15924e759 --- /dev/null +++ b/res/css/views/messages/_ViewSourceEvent.scss @@ -0,0 +1,50 @@ +/* +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_EventTile_content.mx_ViewSourceEvent { + display: flex; + opacity: 0.6; + font-size: 12px; + + pre, code { + flex: 1; + } + + pre { + line-height: 1.2; + margin: 3.5px 0; + } + + .mx_ViewSourceEvent_toggle { + width: 12px; + mask-repeat: no-repeat; + mask-position: 0 center; + mask-size: auto 12px; + visibility: hidden; + background-color: $accent-color; + mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); + } + + &.mx_ViewSourceEvent_expanded .mx_ViewSourceEvent_toggle { + mask-position: 0 bottom; + margin-bottom: 7px; + mask-image: url('$(res)/img/feather-customised/widget/minimise.svg'); + } + + &:hover .mx_ViewSourceEvent_toggle { + visibility: visible; + } +} diff --git a/res/css/views/room_settings/_AliasSettings.scss b/res/css/views/room_settings/_AliasSettings.scss new file mode 100644 index 0000000000..d4ae58e5b0 --- /dev/null +++ b/res/css/views/room_settings/_AliasSettings.scss @@ -0,0 +1,28 @@ +/* +Copyright 2019 New Vector Ltd. + +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_AliasSettings_editable { + border: 0px; + border-bottom: 1px solid $strong-input-border-color; + padding: 0px; + min-width: 240px; +} + +.mx_AliasSettings_editable:focus { + border-bottom: 1px solid $accent-color; + outline: none; + box-shadow: none; +} diff --git a/res/css/views/room_settings/_ColorSettings.scss b/res/css/views/room_settings/_ColorSettings.scss new file mode 100644 index 0000000000..39b087653d --- /dev/null +++ b/res/css/views/room_settings/_ColorSettings.scss @@ -0,0 +1,39 @@ +/* +Copyright 2019 New Vector Ltd. + +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_ColorSettings_roomColor { + display: inline-block; + position: relative; + width: 37px; + height: 37px; + border: 1px solid #979797; + margin-right: 13px; + cursor: pointer; +} + +.mx_ColorSettings_roomColor_selected { + position: absolute; + left: 10px; + top: 4px; + cursor: default ! important; +} + +.mx_ColorSettings_roomColorPrimary { + height: 10px; + position: absolute; + bottom: 0px; + width: 100%; +} diff --git a/res/css/views/rooms/_AppsDrawer.scss b/res/css/views/rooms/_AppsDrawer.scss index 8094062628..db38eebfca 100644 --- a/res/css/views/rooms/_AppsDrawer.scss +++ b/res/css/views/rooms/_AppsDrawer.scss @@ -14,6 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +/* +the tile title bar is 5 (top border) + 12 (title, buttons) + 5 (bottom padding) px = 22px +the body is assumed to be 300px (assumed by at least the sticker pickerm, perhaps elsewhere), +so the body height would be 300px - 22px (room for title bar) = 278px +BUT! the sticker picker also assumes it's a little less high than that because the iframe +for the sticker picker doesn't have any padding or margin on it's bottom. +so subtracking another 5px, which brings us at 273px. +*/ +$AppsDrawerBodyHeight: 273px; + .mx_AppsDrawer { margin: 5px; } @@ -83,7 +93,7 @@ limitations under the License. } .mx_AppTile_persistedWrapper { - height: 280px; + height: $AppsDrawerBodyHeight; } .mx_AppTile_mini .mx_AppTile_persistedWrapper { @@ -134,39 +144,39 @@ limitations under the License. } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_minimise { - mask-image: url('$(res)/img/feather-icons/widget/minimise.svg'); + mask-image: url('$(res)/img/feather-customised/widget/minimise.svg'); background-color: $accent-color; } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_maximise { - mask-image: url('$(res)/img/feather-icons/widget/maximise.svg'); + mask-image: url('$(res)/img/feather-customised/widget/maximise.svg'); background-color: $accent-color; } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_reload { - mask-image: url('$(res)/img/feather-icons/widget/refresh.svg'); + mask-image: url('$(res)/img/feather-customised/widget/refresh.svg'); mask-size: 100%; } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_popout { - mask-image: url('$(res)/img/feather-icons/widget/external-link.svg'); + mask-image: url('$(res)/img/feather-customised/widget/external-link.svg'); } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_snapshot { - mask-image: url('$(res)/img/feather-icons/widget/camera.svg'); + mask-image: url('$(res)/img/feather-customised/widget/camera.svg'); } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_edit { - mask-image: url('$(res)/img/feather-icons/widget/edit.svg'); + mask-image: url('$(res)/img/feather-customised/widget/edit.svg'); } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_delete { - mask-image: url('$(res)/img/feather-icons/widget/bin.svg'); + mask-image: url('$(res)/img/feather-customised/widget/bin.svg'); background-color: $warning-color; } .mx_AppTileMenuBar_iconButton.mx_AppTileMenuBar_iconButton_cancel { - mask-image: url('$(res)/img/feather-icons/widget/x-circle.svg'); + mask-image: url('$(res)/img/feather-customised/widget/x-circle.svg'); } /* delete ? */ @@ -189,7 +199,7 @@ limitations under the License. } .mx_AppTileBody{ - height: 280px; + height: $AppsDrawerBodyHeight; width: 100%; overflow: hidden; } @@ -208,7 +218,7 @@ limitations under the License. .mx_AppTileBody iframe { width: 100%; - height: 280px; + height: $AppsDrawerBodyHeight; overflow: hidden; border: none; padding: 0; @@ -332,7 +342,7 @@ form.mx_Custom_Widget_Form div { align-items: center; font-weight: bold; position: relative; - height: 280px; + height: $AppsDrawerBodyHeight; } .mx_AppLoading .mx_Spinner { diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index 3e1016f60d..3bc0ea25a4 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -71,7 +71,7 @@ .mx_Autocomplete_Completion.selected, .mx_Autocomplete_Completion:hover { - background: $menu-bg-color; + background: $selected-color; outline: none; } diff --git a/res/css/views/rooms/_E2EIcon.scss b/res/css/views/rooms/_E2EIcon.scss index cd577df87b..84a16611de 100644 --- a/res/css/views/rooms/_E2EIcon.scss +++ b/res/css/views/rooms/_E2EIcon.scss @@ -23,11 +23,11 @@ limitations under the License. } .mx_E2EIcon_verified { - mask-image: url('$(res)/img/feather-icons/e2e/lock-verified.svg'); + mask-image: url('$(res)/img/e2e/lock-verified.svg'); background-color: $accent-color; } .mx_E2EIcon_warning { - mask-image: url('$(res)/img/feather-icons/e2e/lock-warning.svg'); + mask-image: url('$(res)/img/e2e/lock-warning.svg'); background-color: $warning-color; } diff --git a/res/css/views/rooms/_EntityTile.scss b/res/css/views/rooms/_EntityTile.scss index 80b8126b54..44528a5624 100644 --- a/res/css/views/rooms/_EntityTile.scss +++ b/res/css/views/rooms/_EntityTile.scss @@ -22,10 +22,28 @@ limitations under the License. } .mx_EntityTile:hover { - background-image: url('$(res)/img/member_chevron.png'); - background-position: center right 10px; - background-repeat: no-repeat; padding-right: 30px; + position: relative; // to keep the chevron aligned +} + +.mx_EntityTile:hover::before { + content: ""; + position: absolute; + top: calc(50% - 8px); // center + right: 10px; + mask: url('$(res)/img/member_chevron.png'); + mask-repeat: no-repeat; + width: 16px; + height: 16px; + background-color: $rightpanel-button-color; +} + +.mx_EntityTile .mx_PresenceLabel { + display: none; +} + +.mx_EntityTile:not(.mx_EntityTile_noHover):hover .mx_PresenceLabel { + display: block; } .mx_EntityTile_invite { @@ -67,7 +85,7 @@ limitations under the License. overflow: hidden; } -.mx_EntityTile_name_hover { +.mx_EntityTile:not(.mx_EntityTile_noHover):hover .mx_EntityTile_name { font-size: 13px; } @@ -81,31 +99,25 @@ limitations under the License. color: $primary-fg-color; } -/* .mx_EntityTile_unavailable .mx_EntityTile_avatar, .mx_EntityTile_unavailable .mx_EntityTile_name, -.mx_EntityTile_unavailable .mx_EntityTile_name_hover, .mx_EntityTile_offline_beenactive .mx_EntityTile_avatar, -.mx_EntityTile_offline_beenactive .mx_EntityTile_name, -.mx_EntityTile_offline_beenactive .mx_EntityTile_name_hover +.mx_EntityTile_offline_beenactive .mx_EntityTile_name { - opacity: 0.66; + opacity: 0.5; } .mx_EntityTile_offline_neveractive .mx_EntityTile_avatar, -.mx_EntityTile_offline_neveractive .mx_EntityTile_name, -.mx_EntityTile_offline_neveractive .mx_EntityTile_name_hover +.mx_EntityTile_offline_neveractive .mx_EntityTile_name { opacity: 0.25; } .mx_EntityTile_unknown .mx_EntityTile_avatar, -.mx_EntityTile_unknown .mx_EntityTile_name, -.mx_EntityTile_unknown .mx_EntityTile_name_hover +.mx_EntityTile_unknown .mx_EntityTile_name { opacity: 0.25; } -*/ .mx_EntityTile_subtext { font-size: 11px; diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 7c9a6babea..72881231f8 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -31,6 +31,7 @@ limitations under the License. top: 14px; left: 8px; cursor: pointer; + user-select: none; } .mx_EventTile.mx_EventTile_info .mx_EventTile_avatar { @@ -39,31 +40,49 @@ limitations under the License. } .mx_EventTile_continuation { - padding-top: 0px ! important; + padding-top: 0px !important; + + &.mx_EventTile_isEditing { + padding-top: 5px !important; + margin-top: -5px; + } +} + +.mx_EventTile_isEditing { + background-color: $header-panel-bg-color; } .mx_EventTile .mx_SenderProfile { color: $primary-fg-color; font-size: 14px; - display: block; /* anti-zalgo, with overflow hidden */ + display: inline-block; /* anti-zalgo, with overflow hidden */ overflow-y: hidden; cursor: pointer; padding-left: 65px; /* left gutter */ padding-bottom: 0px; padding-top: 0px; margin: 0px; - line-height: 22px; + line-height: 17px; } .mx_EventTile .mx_SenderProfile .mx_Flair { opacity: 0.7; margin-left: 5px; + display: inline-block; + vertical-align: top; + height: 16px; + overflow: hidden; + user-select: none; + + img { + vertical-align: -2px; + margin-right: 2px; + border-radius: 8px; + } } -.mx_EventTile .mx_SenderProfile .mx_Flair img { - vertical-align: -2px; - margin-right: 2px; - border-radius: 8px; +.mx_EventTile_isEditing .mx_MessageTimestamp { + visibility: hidden !important; } .mx_EventTile .mx_MessageTimestamp { @@ -76,6 +95,7 @@ limitations under the License. width: 46px; /* 8 + 30 (avatar) + 8 */ text-align: center; position: absolute; + user-select: none; } .mx_EventTile_line, .mx_EventTile_reply { @@ -101,7 +121,7 @@ limitations under the License. /* HACK to override line-height which is already marked important elsewhere */ .mx_EventTile_bigEmoji.mx_EventTile_bigEmoji { font-size: 48px ! important; - line-height: 48px ! important; + line-height: 52px ! important; } /* this is used for the tile for the event which is selected via the URL. @@ -114,7 +134,7 @@ limitations under the License. } .mx_EventTile:hover .mx_EventTile_line, -.mx_EventTile.menu .mx_EventTile_line +.mx_EventTile.mx_EventTile_actionBarFocused .mx_EventTile_line { background-color: $event-selected-color; } @@ -142,8 +162,7 @@ limitations under the License. } .mx_EventTile_sending .mx_UserPill, -.mx_EventTile_sending .mx_RoomPill, -.mx_EventTile_sending .mx_emojione { +.mx_EventTile_sending .mx_RoomPill { opacity: 0.5; } @@ -199,7 +218,7 @@ limitations under the License. // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) .mx_EventTile_last > div > a > .mx_MessageTimestamp, .mx_EventTile:hover > div > a > .mx_MessageTimestamp, -.mx_EventTile.menu > div > a > .mx_MessageTimestamp { +.mx_EventTile.mx_EventTile_actionBarFocused > div > a > .mx_MessageTimestamp { visibility: visible; } @@ -212,24 +231,8 @@ limitations under the License. width: auto; } -.mx_EventTile_editButton { - position: absolute; - display: inline-block; - visibility: hidden; - cursor: pointer; - top: 6px; - right: 6px; - width: 19px; - height: 19px; - background-image: url($edit-button-url); - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.mx_EventTile:hover .mx_EventTile_editButton, -.mx_EventTile.menu .mx_EventTile_editButton { +.mx_EventTile:hover .mx_MessageActionBar, +.mx_EventTile.mx_EventTile_actionBarFocused .mx_MessageActionBar { visibility: visible; } @@ -239,6 +242,7 @@ limitations under the License. width: 14px; height: 14px; top: 29px; + user-select: none; } .mx_EventTile_continuation .mx_EventTile_readAvatars, @@ -277,6 +281,9 @@ limitations under the License. } /* End to end encryption stuff */ +.mx_EventTile:hover .mx_EventTile_e2eIcon { + opacity: 1; +} .mx_EventTile_e2eIcon { display: block; @@ -289,15 +296,16 @@ limitations under the License. mask-size: 14px; mask-repeat: no-repeat; mask-position: 0; + opacity: 0.2; } .mx_EventTile_e2eIcon_undecryptable, .mx_EventTile_e2eIcon_unverified { - mask-image: url('$(res)/img/feather-icons/e2e/warning.svg'); + mask-image: url('$(res)/img/e2e/warning.svg'); background-color: $warning-color; } .mx_EventTile_e2eIcon_unencrypted { - mask-image: url('$(res)/img/feather-icons/e2e/warning.svg'); + mask-image: url('$(res)/img/e2e/warning.svg'); background-color: $composer-e2e-icon-color; } @@ -381,6 +389,14 @@ limitations under the License. left: 41px; } +.mx_EventTile_content .mx_EventTile_edited { + user-select: none; + font-size: 12px; + color: $roomtopic-color; + display: inline-block; + margin-left: 9px; +} + /* Various markdown overrides */ .mx_EventTile_content .markdown-body { @@ -404,7 +420,14 @@ limitations under the License. .mx_EventTile_content .markdown-body code { // deliberate constants as we're behind an invert filter background-color: #f8f8f8; - color: #333; +} + +.mx_EventTile_content .markdown-body { + pre, code { + font-family: $monospace-font-family ! important; + // deliberate constants as we're behind an invert filter + color: #333; + } } .mx_EventTile_pre_container { @@ -533,10 +556,6 @@ limitations under the License. } .mx_EventTile_e2eIcon { - top: 7px; - } - - .mx_EventTile_editButton { top: 3px; } diff --git a/res/css/views/rooms/_MemberDeviceInfo.scss b/res/css/views/rooms/_MemberDeviceInfo.scss index f780c50410..3be6a0f7b4 100644 --- a/res/css/views/rooms/_MemberDeviceInfo.scss +++ b/res/css/views/rooms/_MemberDeviceInfo.scss @@ -27,15 +27,15 @@ limitations under the License. mask-repeat: no-repeat; } .mx_MemberDeviceInfo_icon_blacklisted { - mask-image: url('$(res)/img/feather-icons/e2e/blacklisted.svg'); + mask-image: url('$(res)/img/e2e/blacklisted.svg'); background-color: $warning-color; } .mx_MemberDeviceInfo_icon_verified { - mask-image: url('$(res)/img/feather-icons/e2e/verified.svg'); + mask-image: url('$(res)/img/e2e/verified.svg'); background-color: $accent-color; } .mx_MemberDeviceInfo_icon_unverified { - mask-image: url('$(res)/img/feather-icons/e2e/warning.svg'); + mask-image: url('$(res)/img/e2e/warning.svg'); background-color: $warning-color; } diff --git a/res/css/views/rooms/_MemberInfo.scss b/res/css/views/rooms/_MemberInfo.scss index 707c186518..c3b3ca2f7d 100644 --- a/res/css/views/rooms/_MemberInfo.scss +++ b/res/css/views/rooms/_MemberInfo.scss @@ -27,13 +27,18 @@ limitations under the License. } .mx_MemberInfo_name > .mx_E2EIcon { - margin-left: 0; + margin-right: 0; } .mx_MemberInfo_cancel { height: 16px; - padding: 10px 15px; + width: 16px; + padding: 10px 0 10px 10px; cursor: pointer; + mask-image: url('$(res)/img/minimise.svg'); + mask-repeat: no-repeat; + mask-position: 16px center; + background-color: $rightpanel-button-color; } .mx_MemberInfo_name h2 { @@ -43,7 +48,7 @@ limitations under the License. .mx_MemberInfo h2 { font-size: 18px; font-weight: 600; - margin: 16px 0; + margin: 16px 0 16px 15px; } .mx_MemberInfo_container { diff --git a/res/css/views/rooms/_MemberList.scss b/res/css/views/rooms/_MemberList.scss index bbcc774665..0b9c7e2368 100644 --- a/res/css/views/rooms/_MemberList.scss +++ b/res/css/views/rooms/_MemberList.scss @@ -20,6 +20,7 @@ limitations under the License. flex: 1; display: flex; flex-direction: column; + min-height: 0; .mx_Spinner { flex: 1 0 auto; @@ -35,6 +36,10 @@ limitations under the License. margin-top: 8px; margin-bottom: 4px; } + + .mx_AutoHideScrollbar { + flex: 1 1 0; + } } .mx_MemberList_chevron { @@ -82,8 +87,13 @@ limitations under the License. } } +.mx_MemberList_invite.mx_AccessibleButton_disabled { + background-color: $greyed-fg-color;; + cursor: not-allowed; +} + .mx_MemberList_invite span { - background-image: url('$(res)/img/feather-icons/user-add.svg'); + background-image: url('$(res)/img/feather-customised/user-add.svg'); background-repeat: no-repeat; background-position: center left; padding-left: 25px; diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 7ee7efcaff..708c29bb3e 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -180,27 +180,36 @@ limitations under the License. color: $accent-color; } -.mx_MessageComposer_upload, -.mx_MessageComposer_hangup, -.mx_MessageComposer_voicecall, -.mx_MessageComposer_videocall, -.mx_MessageComposer_apps, -.mx_MessageComposer_stickers { - /*display: table-cell;*/ - /*vertical-align: middle;*/ - /*padding-left: 10px;*/ - padding-right: 12px; +.mx_MessageComposer_button { + margin-right: 12px; cursor: pointer; padding-top: 4px; + height: 20px; + width: 20px; + background-color: $composer-button-color; + mask-repeat: no-repeat; + mask-size: contain; + mask-position: center; } -.mx_MessageComposer_upload object, -.mx_MessageComposer_hangup object, -.mx_MessageComposer_voicecall object, -.mx_MessageComposer_videocall object, -.mx_MessageComposer_apps object, -.mx_MessageComposer_stickers object { - pointer-events: none; +.mx_MessageComposer_upload { + mask-image: url('$(res)/img/feather-customised/paperclip.svg'); +} + +.mx_MessageComposer_hangup { + mask-image: url('$(res)/img/hangup.svg'); +} + +.mx_MessageComposer_voicecall { + mask-image: url('$(res)/img/feather-customised/phone.svg'); +} + +.mx_MessageComposer_videocall { + mask-image: url('$(res)/img/feather-customised/video.svg'); +} + +.mx_MessageComposer_stickers { + mask-image: url('$(res)/img/feather-customised/face.svg'); } .mx_MessageComposer_formatting { @@ -246,14 +255,29 @@ limitations under the License. } .mx_MessageComposer_formatbar_markdown { + height: 17px; + width: 30px; margin-right: 64px; } .mx_MessageComposer_input_markdownIndicator { - cursor: pointer; height: 10px; + width: 12px; padding: 4px 4px 4px 0; - opacity: 0.8; +} + +.mx_MessageComposer_formatbar_markdown, +.mx_MessageComposer_input_markdownIndicator { + cursor: pointer; + mask-image: url('$(res)/img/markdown.svg'); + mask-size: contain; + mask-position: center; + mask-repeat: no-repeat; + background-color: $composer-button-color; + + &.mx_MessageComposer_markdownDisabled { + opacity: 0.2; + } } .mx_MatrixChat_useCompactLayout { diff --git a/res/css/views/rooms/_RoomBreadcrumbs.scss b/res/css/views/rooms/_RoomBreadcrumbs.scss new file mode 100644 index 0000000000..6c3eb0420a --- /dev/null +++ b/res/css/views/rooms/_RoomBreadcrumbs.scss @@ -0,0 +1,100 @@ +/* +Copyright 2019 New Vector Ltd + +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_RoomBreadcrumbs { + position: relative; + height: 42px; + padding: 8px; + padding-bottom: 0; + display: flex; + flex-direction: row; + + // Autohide the scrollbar + overflow-x: hidden; + &:hover { + overflow-x: visible; + } + + .mx_AutoHideScrollbar_offset { + display: flex; + flex-direction: row; + height: 100%; + } + + .mx_RoomBreadcrumbs_crumb { + margin-left: 4px; + height: 32px; + display: inline-block; + transition: transform 0.3s, width 0.3s; + position: relative; + + .mx_RoomTile_badge { + position: absolute; + top: -3px; + right: -4px; + } + + .mx_RoomBreadcrumbs_dmIndicator { + position: absolute; + bottom: 0; + right: -4px; + } + } + + .mx_RoomBreadcrumbs_animate { + margin-left: 0; + width: 32px; + transform: scale(1); + } + + .mx_RoomBreadcrumbs_preAnimate { + width: 0; + transform: scale(0); + } + + .mx_RoomBreadcrumbs_left { + opacity: 0.5; + } + + // Note: we have to manually control the gradient and stuff, but the IndicatorScrollbar + // will deal with left/right positioning for us. Normally we'd use position:sticky on + // a few key elements, however that doesn't work in horizontal scrolling scenarios. + + .mx_IndicatorScrollbar_leftOverflowIndicator, + .mx_IndicatorScrollbar_rightOverflowIndicator { + display: none; + } + + &.mx_IndicatorScrollbar_leftOverflow .mx_IndicatorScrollbar_leftOverflowIndicator, + &.mx_IndicatorScrollbar_rightOverflow .mx_IndicatorScrollbar_rightOverflowIndicator { + position: absolute; + top: 0; + bottom: 0; + width: 15px; + display: block; + pointer-events: none; + z-index: 100; + } + + .mx_IndicatorScrollbar_leftOverflowIndicator { + background: linear-gradient(to left, $panel-gradient); + } + + .mx_IndicatorScrollbar_rightOverflowIndicator { + background: linear-gradient(to right, $panel-gradient); + } +} + diff --git a/res/css/views/rooms/_RoomHeader.scss b/res/css/views/rooms/_RoomHeader.scss index 1359bc5f57..e7589f0e90 100644 --- a/res/css/views/rooms/_RoomHeader.scss +++ b/res/css/views/rooms/_RoomHeader.scss @@ -58,7 +58,6 @@ limitations under the License. .mx_RoomHeader_buttons { display: flex; - align-items: center; background-color: $primary-bg-color; padding-right: 5px; } @@ -116,10 +115,6 @@ limitations under the License. opacity: 0.6; } -.mx_RoomHeader_settingsButton object { - pointer-events: none; -} - .mx_RoomHeader_name, .mx_RoomHeader_avatar, .mx_RoomHeader_avatarPicker, @@ -160,6 +155,7 @@ limitations under the License. font-weight: 400; font-size: 13px; margin: 0 7px; + margin-top: 4px; // to align baseline of topic with room name overflow: hidden; text-overflow: ellipsis; border-bottom: 1px solid transparent; @@ -199,10 +195,32 @@ limitations under the License. .mx_RoomHeader_button { margin-left: 10px; cursor: pointer; + height: 20px; + width: 20px; + background-color: $roomheader-button-color; + mask-repeat: no-repeat; + mask-size: contain; } -.mx_RoomHeader_button object { - pointer-events: none; +.mx_RoomHeader_settingsButton { + mask-image: url('$(res)/img/feather-customised/settings.svg'); +} + +.mx_RoomHeader_forgetButton { + mask-image: url('$(res)/img/leave.svg'); + width: 26px; +} + +.mx_RoomHeader_searchButton { + mask-image: url('$(res)/img/feather-customised/search.svg'); +} + +.mx_RoomHeader_shareButton { + mask-image: url('$(res)/img/feather-customised/share.svg'); +} + +.mx_RoomHeader_manageIntegsButton { + mask-image: url('$(res)/img/feather-customised/grid.svg'); } .mx_RoomHeader_showPanel { @@ -219,6 +237,7 @@ limitations under the License. .mx_RoomHeader_pinnedButton { position: relative; + mask-image: url('$(res)/img/icons-pin.svg'); } .mx_RoomHeader_pinsIndicator { diff --git a/res/css/views/rooms/_RoomPreviewBar.scss b/res/css/views/rooms/_RoomPreviewBar.scss index 8196740499..ea3b787971 100644 --- a/res/css/views/rooms/_RoomPreviewBar.scss +++ b/res/css/views/rooms/_RoomPreviewBar.scss @@ -15,48 +15,112 @@ limitations under the License. */ .mx_RoomPreviewBar { - text-align: center; - height: 176px; - background-color: $event-selected-color; + flex: 0 0 auto; align-items: center; flex-direction: column; justify-content: center; display: flex; - background-color: $preview-bar-bg-color; -webkit-align-items: center; + + h3 { + font-size: 18px; + font-weight: 600; + + &.mx_RoomPreviewBar_spinnerTitle { + display: flex; + flex-direction: row; + align-items: center; + } + } + + .mx_Spinner { + width: auto; + height: auto; + margin: 10px 10px 10px 0; + flex: 0 0 auto; + } } -.mx_RoomPreviewBar_wrapper { +.mx_RoomPreviewBar_dark { + background-color: $tagpanel-bg-color; + color: $accent-fg-color; } -.mx_RoomPreviewBar_invite_text { - color: $primary-fg-color; +.mx_RoomPreviewBar_actions { + display: flex; } -.mx_RoomPreviewBar_join_text { - color: $warning-color; +.mx_RoomPreviewBar_message { + display: flex; + align-items: stretch; + + p { + overflow-wrap: break-word; + } } -.mx_RoomPreviewBar_preview_text { - margin-top: 25px; - color: $settings-grey-fg-color; +.mx_RoomPreviewBar_panel { + padding: 8px 8px 8px 20px; + border-top: 1px solid $panel-divider-color; + + flex-direction: row; + + .mx_RoomPreviewBar_actions { + flex: 0 0 auto; + flex-direction: row; + padding: 3px 8px; + + &>* { + margin-left: 12px; + } + } + + .mx_RoomPreviewBar_message { + flex: 1 0 0; + min-width: 0; + display: flex; + flex-direction: column; + + &>* { + margin: 4px; + } + } } -.mx_RoomPreviewBar_join_text a { +.mx_RoomPreviewBar_dialog { + margin: auto; + box-sizing: content; + width: 400px; + border-radius: 4px; + flex-direction: column; + padding: 20px; + text-align: center; + + .mx_RoomPreviewBar_message { + flex-direction: column; + + &>* { + margin: 5px 0 20px 0; + } + } + + .mx_RoomPreviewBar_actions { + flex-direction: column-reverse; + .mx_AccessibleButton { + padding: 7px 50px;//extra wide + } + + &>* { + margin-top: 12px; + } + } +} + +.mx_RoomPreviewBar_inviter { + font-weight: 600; +} + +a.mx_RoomPreviewBar_inviter { text-decoration: underline; cursor: pointer; } - -.mx_RoomPreviewBar_warning { - display: flex; - align-items: center; - padding: 8px; -} - -.mx_RoomPreviewBar_warningIcon { - padding: 12px; -} - -.mx_RoomPreviewBar_spinnerIntro { - margin-top: 50px; -} \ No newline at end of file diff --git a/res/css/views/rooms/_RoomRecoveryReminder.scss b/res/css/views/rooms/_RoomRecoveryReminder.scss index e4e2d19b42..68e2bf861e 100644 --- a/res/css/views/rooms/_RoomRecoveryReminder.scss +++ b/res/css/views/rooms/_RoomRecoveryReminder.scss @@ -38,7 +38,6 @@ limitations under the License. margin: 0 10px; } -.mx_RoomRecoveryReminder_button.mx_RoomRecoveryReminder_secondary { - @mixin mx_DialogButton_secondary; - background-color: transparent; +.mx_RoomRecoveryReminder_secondary { + font-size: 90%; } diff --git a/res/css/views/rooms/_RoomSettings.scss b/res/css/views/rooms/_RoomSettings.scss deleted file mode 100644 index 5ed9168aab..0000000000 --- a/res/css/views/rooms/_RoomSettings.scss +++ /dev/null @@ -1,260 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd - -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_RoomSettings { - margin: 40px; -} - -.mx_RoomSettings_upgradeButton, -.mx_RoomSettings_leaveButton, -.mx_RoomSettings_unbanButton { - @mixin mx_DialogButton; - position: relative; - margin-right: 8px; -} - -.mx_RoomSettings_devtoolsButton { - @mixin mx_DialogButton; - position: relative; - padding: 4px 1.5em; - margin-top: 8px; -} - -.mx_RoomSettings_upgradeButton, -.mx_RoomSettings_leaveButton:hover, -.mx_RoomSettings_unbanButton:hover { - @mixin mx_DialogButton_hover; -} - -.mx_RoomSettings_upgradeButton.danger { - @mixin mx_DialogButton_danger; -} - -.mx_RoomSettings_integrationsButton_error { - position: relative; - cursor: not-allowed; -} -.mx_RoomSettings_integrationsButton_error img { - position: absolute; - right: -5px; - top: -5px; -} -.mx_RoomSettings_leaveButton, -.mx_RoomSettings_integrationsButton_error { - float: right; -} -.mx_RoomSettings_integrationsButton_error .mx_RoomSettings_integrationsButton_errorPopup { - display: none; -} -.mx_RoomSettings_integrationsButton_error:hover .mx_RoomSettings_integrationsButton_errorPopup { - display: inline; -} -.mx_RoomSettings_integrationsButton_errorPopup { - position: absolute; - top: 110%; - left: -275%; - width: 550%; - padding: 30%; - font-size: 10pt; - line-height: 1.5em; - border-radius: 5px; - background-color: $accent-color; - color: $accent-fg-color; - text-align: center; - z-index: 1000; -} -.mx_RoomSettings_unbanButton { - display: inline; -} - -.mx_RoomSettings_e2eIcon { - padding-left: 4px; - padding-right: 7px; -} - -.mx_RoomSettings_leaveButton { - margin-right: 32px; -} - -.mx_RoomSettings_powerLevels { - display: table; -} - -.mx_RoomSettings_powerLevel { - display: table-row; -} - -.mx_RoomSettings_powerLevelKey, -.mx_RoomSettings_powerLevel .mx_PowerSelector { - display: table-cell; - padding-bottom: 5px; -} - -.mx_RoomSettings_powerLevelKey { - text-align: right; - padding-right: 0.3em; -} - -.mx_RoomSettings h3 { - text-transform: uppercase; - color: $h3-color; - font-weight: 600; - font-size: 13px; - margin-top: 36px; - margin-bottom: 10px; -} - -.mx_RoomSettings .mx_RoomSettings_toggles label { - margin-bottom: 8px; - display: block; -} - -.mx_RoomSettings .mx_RoomSettings_toggles input[type="checkbox"], -.mx_RoomSettings .mx_RoomSettings_toggles input[type="radio"] { - margin-right: 7px; -} - -.mx_RoomSettings .mx_RoomSettings_tags input[type="checkbox"] { - margin-left: 1em; - margin-right: 7px; -} - -.mx_RoomSettings .mx_RoomSettings_tags { - margin-bottom: 8px; -} - -.mx_RoomSettings .mx_RoomSettings_roomColor { - display: inline-block; - position: relative; - width: 37px; - height: 37px; - border: 1px solid #979797; - margin-right: 13px; - cursor: pointer; -} - -.mx_RoomSettings .mx_RoomSettings_roomColor_selected { - position: absolute; - left: 10px; - top: 4px; - cursor: default ! important; -} - -.mx_RoomSettings .mx_RoomSettings_roomColorPrimary { - height: 10px; - position: absolute; - bottom: 0px; - width: 100%; -} - -.mx_RoomSettings .mx_RoomSettings_aliasLabel { - margin-bottom: 8px; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTable { - margin-top: 12px; - margin-bottom: 0px; - margin-left: 56px; - display: table; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow { - display: table-row; - margin-bottom: 16px; -} - -.mx_RoomSettings .mx_RoomSettings_alias { - max-width: 400px; - margin-bottom: 16px; - /* - commented out so margin applies - display: table-cell; */ -} - -.mx_RoomSettings .mx_RoomSettings_addAlias, -.mx_RoomSettings .mx_RoomSettings_deleteAlias { - display: table-cell; - padding-left: 0.5em; - position: relative; - cursor: pointer; -} - -.mx_RoomSettings .mx_RoomSettings_addAlias img, -.mx_RoomSettings .mx_RoomSettings_deleteAlias img { - visibility: hidden; -} - -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_addAlias img, -.mx_RoomSettings .mx_RoomSettings_aliasesTableRow:hover .mx_RoomSettings_deleteAlias img { - visibility: visible; -} - -.mx_RoomSettings_warning { - color: $warning-color; - font-weight: bold; - margin-top: 8px; - margin-bottom: 8px; -} - -.mx_RoomSettings_editable { - border: 0px; - border-bottom: 1px solid $strong-input-border-color; - padding: 0px; - min-width: 240px; -} - -.mx_RoomSettings_editable:focus { - border-bottom: 1px solid $accent-color; - outline: none; - box-shadow: none; -} - -.mx_RoomSettings_deleteAlias, -.mx_RoomSettings_addAlias { - display: table-cell; - visibility: visible; -} - -.mx_RoomSettings_deleteAlias:hover, -.mx_RoomSettings_addAlias:hover { - visibility: visible; -} - -.mx_RoomSettings_aliasPlaceholder { - color: $settings-grey-fg-color; -} - -.mx_RoomSettings_buttons { - text-align: right; - margin-bottom: 16px; -} - -.mx_RoomSettings_button { - display: inline; - border: 0px; - height: 36px; - border-radius: 36px; - font-weight: 400; - font-size: 15px; - color: $accent-fg-color; - background-color: $accent-color; - width: auto; - margin: auto; - padding: 6px; - padding-left: 1em; - padding-right: 1em; -} diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 220790784e..a1fc9bdca1 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -102,7 +102,6 @@ limitations under the License. .mx_RoomTile_name { font-size: 14px; - font-weight: 600; padding: 0 6px; color: $roomtile-name-color; white-space: nowrap; @@ -145,18 +144,20 @@ limitations under the License. font-size: 12px; } -.mx_RoomTile_unreadNotify .mx_RoomTile_badge { +.mx_RoomTile_unreadNotify .mx_RoomTile_badge, +.mx_RoomTile_badge.mx_RoomTile_badgeUnread { background-color: $roomtile-name-color; } -.mx_RoomTile_highlight .mx_RoomTile_badge { +.mx_RoomTile_highlight .mx_RoomTile_badge, +.mx_RoomTile_badge.mx_RoomTile_badgeRed +{ background-color: $warning-color; } .mx_RoomTile_unread, .mx_RoomTile_highlight { - font-weight: 700 ! important; - .mx_RoomTile_name { + font-weight: 600; color: $roomtile-selected-color; } } @@ -176,7 +177,7 @@ limitations under the License. } .mx_RoomTile:focus { - filter: none ! important; + filter: none !important; background-color: $roomtile-focused-bg-color; } @@ -192,3 +193,7 @@ limitations under the License. .mx_RoomTile.mx_RoomTile_transparent:focus { background-color: $roomtile-transparent-focused-color; } + +.mx_GroupInviteTile .mx_RoomTile_name { + flex: 1; +} diff --git a/res/css/views/rooms/_RoomTooltip.scss b/res/css/views/rooms/_RoomTooltip.scss deleted file mode 100644 index 295786d2d3..0000000000 --- a/res/css/views/rooms/_RoomTooltip.scss +++ /dev/null @@ -1,54 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd - -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_RoomTooltip_chevron { - position: absolute; - left: -8px; - top: 4px; - width: 0; - height: 0; - border-top: 8px solid transparent; - border-right: 8px solid $menu-bg-color; - border-bottom: 8px solid transparent; -} - -.mx_RoomTooltip_chevron:after { - content:''; - width: 0; - height: 0; - border-top: 7px solid transparent; - border-right: 7px solid $primary-bg-color; - border-bottom: 7px solid transparent; - position:absolute; - top: -7px; - left: 1px; -} - -.mx_RoomTooltip { - display: none; - position: fixed; - border-radius: 5px; - box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.21); - background-color: $primary-bg-color; - z-index: 2000; - padding: 5px; - pointer-events: none; - line-height: 14px; - font-size: 13px; - color: $primary-fg-color; - max-width: 600px; - margin-right: 50px; -} diff --git a/res/css/views/rooms/_RoomUpgradeWarningBar.scss b/res/css/views/rooms/_RoomUpgradeWarningBar.scss index 82785b82d2..fe81d3801a 100644 --- a/res/css/views/rooms/_RoomUpgradeWarningBar.scss +++ b/res/css/views/rooms/_RoomUpgradeWarningBar.scss @@ -16,7 +16,7 @@ limitations under the License. .mx_RoomUpgradeWarningBar { text-align: center; - height: 176px; + height: 235px; background-color: $event-selected-color; align-items: center; flex-direction: column; diff --git a/res/css/views/rooms/_SearchBar.scss b/res/css/views/rooms/_SearchBar.scss index b89cb0ce13..894473a5fe 100644 --- a/res/css/views/rooms/_SearchBar.scss +++ b/res/css/views/rooms/_SearchBar.scss @@ -32,7 +32,7 @@ limitations under the License. width: 37px; height: 37px; background-color: $accent-color; - mask: url('$(res)/img/feather-icons/search-input.svg'); + mask: url('$(res)/img/feather-customised/search-input.svg'); mask-repeat: no-repeat; mask-position: center; } diff --git a/res/css/views/rooms/_Stickers.scss b/res/css/views/rooms/_Stickers.scss index 669ca13545..d33ecc0bb6 100644 --- a/res/css/views/rooms/_Stickers.scss +++ b/res/css/views/rooms/_Stickers.scss @@ -7,8 +7,12 @@ height: 300px; } -.mx_Stickers_content .mx_AppTileFullWidth { - border: none; +#mx_persistedElement_stickerPicker .mx_AppTileFullWidth { + height: unset; + box-sizing: border-box; + border-left: none; + border-right: none; + border-bottom: none; } .mx_Stickers_contentPlaceholder { diff --git a/res/css/views/rooms/_WhoIsTypingTile.scss b/res/css/views/rooms/_WhoIsTypingTile.scss index eb51595858..ef20c24c84 100644 --- a/res/css/views/rooms/_WhoIsTypingTile.scss +++ b/res/css/views/rooms/_WhoIsTypingTile.scss @@ -40,6 +40,7 @@ limitations under the License. } .mx_WhoIsTypingTile_remainingAvatarPlaceholder { + position: relative; display: inline-block; color: #acacac; background-color: #ddd; diff --git a/res/css/views/settings/_Notifications.scss b/res/css/views/settings/_Notifications.scss index 171b4c2e05..d6c0b5dbeb 100644 --- a/res/css/views/settings/_Notifications.scss +++ b/res/css/views/settings/_Notifications.scss @@ -59,10 +59,15 @@ limitations under the License. color: $accent-color; } -.mx_UserSettings_devicesTable td { +.mx_UserNotifSettings_devicesTable td { padding-left: 20px; padding-right: 20px; } -.mx_UserSettings_devicesTable_nodevices { - font-style: italic; + +.mx_UserNotifSettings_notifTable { + display: table; +} + +.mx_UserNotifSettings_notifTable .mx_Spinner { + position: absolute; } diff --git a/res/css/views/settings/_ProfileSettings.scss b/res/css/views/settings/_ProfileSettings.scss index 27dfae61a1..b2e449ac34 100644 --- a/res/css/views/settings/_ProfileSettings.scss +++ b/res/css/views/settings/_ProfileSettings.scss @@ -35,6 +35,14 @@ limitations under the License. margin-top: 0; } +.mx_ProfileSettings_hostingSignup { + margin-left: 20px; + + img { + margin-left: 5px; + } +} + .mx_ProfileSettings_avatar { width: 88px; height: 88px; @@ -104,7 +112,7 @@ limitations under the License. .mx_ProfileSettings_avatarOverlayImg:before { background-color: $settings-profile-overlay-placeholder-fg-color; - mask: url("$(res)/img/feather-icons/upload.svg"); + mask: url("$(res)/img/feather-customised/upload.svg"); mask-repeat: no-repeat; mask-size: 14px; mask-position: center; diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index a899aec0fa..def28bfbd2 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_SettingsTab_warningText { + color: $warning-color; +} + .mx_SettingsTab_heading { font-size: 20px; font-weight: 600; @@ -21,7 +25,7 @@ limitations under the License. } .mx_SettingsTab_subheading { - font-size: 14px; + font-size: 16px; display: block; font-family: $font-family; font-weight: 600; @@ -32,7 +36,7 @@ limitations under the License. .mx_SettingsTab_subsectionText { color: $settings-subsection-fg-color; - font-size: 12px; + font-size: 14px; padding-bottom: 12px; display: block; margin: 0 100px 0 0; // Align with the rest of the view @@ -40,16 +44,17 @@ limitations under the License. .mx_SettingsTab_section .mx_SettingsFlag { margin-right: 100px; - height: 25px; margin-bottom: 10px; } .mx_SettingsTab_section .mx_SettingsFlag .mx_SettingsFlag_label { - vertical-align: bottom; + vertical-align: middle; display: inline-block; - font-size: 12px; + font-size: 14px; color: $primary-fg-color; max-width: calc(100% - 48px); // Force word wrap instead of colliding with the switch + box-sizing: border-box; + padding-right: 10px; } .mx_SettingsTab_section .mx_SettingsFlag .mx_ToggleSwitch { @@ -67,3 +72,7 @@ limitations under the License. // give them more visual distinction between the sections. margin-top: 30px; } + +.mx_SettingsTab a { + color: $accent-color-alt; +} \ No newline at end of file diff --git a/res/css/views/settings/tabs/_GeneralRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss similarity index 100% rename from res/css/views/settings/tabs/_GeneralRoomSettingsTab.scss rename to res/css/views/settings/tabs/room/_GeneralRoomSettingsTab.scss diff --git a/res/css/views/settings/tabs/_RolesRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss similarity index 100% rename from res/css/views/settings/tabs/_RolesRoomSettingsTab.scss rename to res/css/views/settings/tabs/room/_RolesRoomSettingsTab.scss diff --git a/res/css/views/settings/tabs/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss similarity index 100% rename from res/css/views/settings/tabs/_SecurityRoomSettingsTab.scss rename to res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss diff --git a/res/css/views/settings/tabs/_GeneralUserSettingsTab.scss b/res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss similarity index 100% rename from res/css/views/settings/tabs/_GeneralUserSettingsTab.scss rename to res/css/views/settings/tabs/user/_GeneralUserSettingsTab.scss diff --git a/res/css/views/settings/tabs/_HelpSettingsTab.scss b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss similarity index 87% rename from res/css/views/settings/tabs/_HelpSettingsTab.scss rename to res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss index 249f06ca95..fa0d0edeb7 100644 --- a/res/css/views/settings/tabs/_HelpSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_HelpUserSettingsTab.scss @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_HelpSettingsTab_debugButton { +.mx_HelpUserSettingsTab_debugButton { margin-bottom: 5px; margin-top: 5px; } -.mx_HelpSettingsTab span.mx_AccessibleButton { +.mx_HelpUserSettingsTab span.mx_AccessibleButton { word-break: break-word; } \ No newline at end of file diff --git a/res/css/views/settings/tabs/_NotificationSettingsTab.scss b/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss similarity index 91% rename from res/css/views/settings/tabs/_NotificationSettingsTab.scss rename to res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss index 8fdb688496..3cebd2958e 100644 --- a/res/css/views/settings/tabs/_NotificationSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_NotificationUserSettingsTab.scss @@ -14,6 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_NotificationSettingsTab .mx_SettingsTab_heading { +.mx_NotificationUserSettingsTab .mx_SettingsTab_heading { margin-bottom: 10px; // Give some spacing between the title and the first elements } \ No newline at end of file diff --git a/res/css/views/settings/tabs/_PreferencesSettingsTab.scss b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss similarity index 89% rename from res/css/views/settings/tabs/_PreferencesSettingsTab.scss rename to res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss index b59b69e63b..f447221b7a 100644 --- a/res/css/views/settings/tabs/_PreferencesSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_PreferencesUserSettingsTab.scss @@ -14,11 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_PreferencesSettingsTab .mx_Field { +.mx_PreferencesUserSettingsTab .mx_Field { margin-right: 100px; // Align with the rest of the controls } -.mx_PreferencesSettingsTab .mx_Field input { +.mx_PreferencesUserSettingsTab .mx_Field input { display: block; // Subtract 10px padding on left and right diff --git a/res/css/views/settings/tabs/_SecuritySettingsTab.scss b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss similarity index 63% rename from res/css/views/settings/tabs/_SecuritySettingsTab.scss rename to res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss index ba357f16c3..1c0a4b5864 100644 --- a/res/css/views/settings/tabs/_SecuritySettingsTab.scss +++ b/res/css/views/settings/tabs/user/_SecurityUserSettingsTab.scss @@ -14,40 +14,44 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_SecuritySettingsTab .mx_DevicesPanel { +.mx_SecurityUserSettingsTab .mx_DevicesPanel { // Normally the panel is 880px, however this can easily overflow the container. // TODO: Fix the table to not be squishy width: auto; max-width: 880px; } -.mx_SecuritySettingsTab_deviceInfo { +.mx_SecurityUserSettingsTab_deviceInfo { display: table; padding-left: 0; } -.mx_SecuritySettingsTab_deviceInfo > li { +.mx_SecurityUserSettingsTab_deviceInfo > li { display: table-row; } -.mx_SecuritySettingsTab_deviceInfo > li > label, -.mx_SecuritySettingsTab_deviceInfo > li > span { +.mx_SecurityUserSettingsTab_deviceInfo > li > label, +.mx_SecurityUserSettingsTab_deviceInfo > li > span { display: table-cell; padding-right: 1em; } -.mx_SecuritySettingsTab_importExportButtons .mx_AccessibleButton { +.mx_SecurityUserSettingsTab_importExportButtons .mx_AccessibleButton { margin-right: 10px; } -.mx_SecuritySettingsTab_importExportButtons { +.mx_SecurityUserSettingsTab_bulkOptions .mx_AccessibleButton { + margin-right: 10px; +} + +.mx_SecurityUserSettingsTab_importExportButtons { margin-bottom: 15px; } -.mx_SecuritySettingsTab_ignoredUser { +.mx_SecurityUserSettingsTab_ignoredUser { margin-bottom: 5px; } -.mx_SecuritySettingsTab_ignoredUser .mx_AccessibleButton { +.mx_SecurityUserSettingsTab_ignoredUser .mx_AccessibleButton { margin-right: 10px; } \ No newline at end of file diff --git a/res/css/views/settings/tabs/_VoiceSettingsTab.scss b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss similarity index 84% rename from res/css/views/settings/tabs/_VoiceSettingsTab.scss rename to res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss index 5ddd57b0e2..f5dba9831e 100644 --- a/res/css/views/settings/tabs/_VoiceSettingsTab.scss +++ b/res/css/views/settings/tabs/user/_VoiceUserSettingsTab.scss @@ -14,15 +14,15 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_VoiceSettingsTab .mx_Field select { +.mx_VoiceUserSettingsTab .mx_Field select { width: 100%; max-width: 100%; } -.mx_VoiceSettingsTab .mx_Field { +.mx_VoiceUserSettingsTab .mx_Field { margin-right: 100px; // align with the rest of the fields } -.mx_VoiceSettingsTab_missingMediaPermissions { +.mx_VoiceUserSettingsTab_missingMediaPermissions { margin-bottom: 15px; } diff --git a/res/css/views/verification/_VerificationShowSas.scss b/res/css/views/verification/_VerificationShowSas.scss new file mode 100644 index 0000000000..a0da7e2539 --- /dev/null +++ b/res/css/views/verification/_VerificationShowSas.scss @@ -0,0 +1,48 @@ +/* +Copyright 2019 New Vector Ltd. + +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_VerificationShowSas_decimalSas { + text-align: center; + font-weight: bold; + padding-left: 3px; + padding-right: 3px; +} + +.mx_VerificationShowSas_decimalSas span { + margin-left: 5px; + margin-right: 5px; +} + +.mx_VerificationShowSas_emojiSas { + text-align: center; +} + +.mx_VerificationShowSas_emojiSas_block { + display: inline-block; + margin-left: 15px; + margin-right: 15px; + text-align: center; + margin-bottom: 20px; +} + +.mx_VerificationShowSas_emojiSas_emoji { + font-size: 48px; +} + +.mx_VerificationShowSas_emojiSas_label { + text-align: center; + font-weight: bold; +} diff --git a/res/fonts/Fira_Mono/FiraMono-Bold.ttf b/res/fonts/Fira_Mono/FiraMono-Bold.ttf deleted file mode 100755 index 4b8b1cfbcb..0000000000 Binary files a/res/fonts/Fira_Mono/FiraMono-Bold.ttf and /dev/null differ diff --git a/res/fonts/Fira_Mono/FiraMono-Regular.ttf b/res/fonts/Fira_Mono/FiraMono-Regular.ttf deleted file mode 100755 index 5238c09eda..0000000000 Binary files a/res/fonts/Fira_Mono/FiraMono-Regular.ttf and /dev/null differ diff --git a/res/fonts/Fira_Mono/OFL.txt b/res/fonts/Fira_Mono/OFL.txt deleted file mode 100755 index ba853c049e..0000000000 --- a/res/fonts/Fira_Mono/OFL.txt +++ /dev/null @@ -1,92 +0,0 @@ -Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A. -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlX5qhExfHwNJU.woff2 b/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlX5qhExfHwNJU.woff2 new file mode 100644 index 0000000000..880f06af78 Binary files /dev/null and b/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlX5qhExfHwNJU.woff2 differ diff --git a/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlZ5qhExfHw.woff2 b/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlZ5qhExfHw.woff2 new file mode 100644 index 0000000000..9fe96559d1 Binary files /dev/null and b/res/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlZ5qhExfHw.woff2 differ diff --git a/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71n5_zaDpwm80E.woff2 b/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71n5_zaDpwm80E.woff2 new file mode 100644 index 0000000000..cd79590d99 Binary files /dev/null and b/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71n5_zaDpwm80E.woff2 differ diff --git a/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71p5_zaDpwm.woff2 b/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71p5_zaDpwm.woff2 new file mode 100644 index 0000000000..cf26d38db4 Binary files /dev/null and b/res/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71p5_zaDpwm.woff2 differ diff --git a/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 new file mode 100644 index 0000000000..70f9b03c4d Binary files /dev/null and b/res/fonts/Twemoji_Mozilla/TwemojiMozilla-colr.woff2 differ diff --git a/res/img/03b381.png b/res/img/03b381.png new file mode 100644 index 0000000000..cf28fc7e59 Binary files /dev/null and b/res/img/03b381.png differ diff --git a/res/img/368bd6.png b/res/img/368bd6.png new file mode 100644 index 0000000000..a2700bd0ae Binary files /dev/null and b/res/img/368bd6.png differ diff --git a/res/img/50e2c2.png b/res/img/50e2c2.png deleted file mode 100644 index ee0f855895..0000000000 Binary files a/res/img/50e2c2.png and /dev/null differ diff --git a/res/img/76cfa6.png b/res/img/76cfa6.png deleted file mode 100644 index de1ea60d54..0000000000 Binary files a/res/img/76cfa6.png and /dev/null differ diff --git a/res/img/80cef4.png b/res/img/80cef4.png deleted file mode 100644 index 637d03f63c..0000000000 Binary files a/res/img/80cef4.png and /dev/null differ diff --git a/res/img/ac3ba8.png b/res/img/ac3ba8.png new file mode 100644 index 0000000000..031471d85a Binary files /dev/null and b/res/img/ac3ba8.png differ diff --git a/res/img/avatar-error.svg b/res/img/avatar-error.svg deleted file mode 100644 index c5e168944c..0000000000 --- a/res/img/avatar-error.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - 5EF602F6-A36C-41EE-BAEC-50801DFD5492 - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/button-md-false.png b/res/img/button-md-false.png deleted file mode 100644 index 6debbccc93..0000000000 Binary files a/res/img/button-md-false.png and /dev/null differ diff --git a/res/img/button-md-false.svg b/res/img/button-md-false.svg deleted file mode 100644 index 6414933d96..0000000000 --- a/res/img/button-md-false.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - - D335F9E8-C813-47D7-B1BE-C8DEF2C8214F - Created with sketchtool. - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/button-md-false@2x.png b/res/img/button-md-false@2x.png deleted file mode 100644 index 497f5385d1..0000000000 Binary files a/res/img/button-md-false@2x.png and /dev/null differ diff --git a/res/img/button-md-false@3x.png b/res/img/button-md-false@3x.png deleted file mode 100644 index 1184e6b351..0000000000 Binary files a/res/img/button-md-false@3x.png and /dev/null differ diff --git a/res/img/button-md-true.png b/res/img/button-md-true.png deleted file mode 100644 index 2e39c55e1e..0000000000 Binary files a/res/img/button-md-true.png and /dev/null differ diff --git a/res/img/button-md-true.svg b/res/img/button-md-true.svg deleted file mode 100644 index 2acc4f675c..0000000000 --- a/res/img/button-md-true.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - 2A63B135-4281-4FBB-A88C-012AE22E9594 - Created with sketchtool. - - - - - - - - - \ No newline at end of file diff --git a/res/img/button-md-true@2x.png b/res/img/button-md-true@2x.png deleted file mode 100644 index ad9067f385..0000000000 Binary files a/res/img/button-md-true@2x.png and /dev/null differ diff --git a/res/img/button-md-true@3x.png b/res/img/button-md-true@3x.png deleted file mode 100644 index d615867dc4..0000000000 Binary files a/res/img/button-md-true@3x.png and /dev/null differ diff --git a/res/img/button-new-window.svg b/res/img/button-new-window.svg deleted file mode 100644 index dd1225e798..0000000000 --- a/res/img/button-new-window.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/res/img/button-refresh.svg b/res/img/button-refresh.svg deleted file mode 100644 index b4990a2147..0000000000 --- a/res/img/button-refresh.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/res/img/call.svg b/res/img/call.svg deleted file mode 100644 index f528f9a24e..0000000000 --- a/res/img/call.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - icons_video - Created with bin/sketchtool. - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/camera_green.svg b/res/img/camera_green.svg deleted file mode 100644 index 5aae5502cd..0000000000 --- a/res/img/camera_green.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - diff --git a/res/img/cancel_green.svg b/res/img/cancel_green.svg deleted file mode 100644 index 2e3d759be2..0000000000 --- a/res/img/cancel_green.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - Slice 1 - Created with Sketch. - - - - - \ No newline at end of file diff --git a/res/img/directory-big.svg b/res/img/directory-big.svg deleted file mode 100644 index 5631a2ae3e..0000000000 --- a/res/img/directory-big.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - icons_directory - Created with sketchtool. - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/e2e-blocked.svg b/res/img/e2e-blocked.svg deleted file mode 100644 index 0ab2c6efbe..0000000000 --- a/res/img/e2e-blocked.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - 2805649B-D39D-43EA-A357-659EF9B97BA4 - Created with sketchtool. - - - - - - - \ No newline at end of file diff --git a/res/img/e2e-encrypting.svg b/res/img/e2e-encrypting.svg deleted file mode 100644 index 469611cc8d..0000000000 --- a/res/img/e2e-encrypting.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - -48BF5D32-306C-4B20-88EB-24B1F743CAC9 -Created with sketchtool. - - - - - - - diff --git a/res/img/e2e-not_sent.svg b/res/img/e2e-not_sent.svg deleted file mode 100644 index fca79ae547..0000000000 --- a/res/img/e2e-not_sent.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - -48BF5D32-306C-4B20-88EB-24B1F743CAC9 -Created with sketchtool. - - - - - - - diff --git a/res/img/e2e-unencrypted.svg b/res/img/e2e-unencrypted.svg deleted file mode 100644 index 1467223638..0000000000 --- a/res/img/e2e-unencrypted.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - 16F5F38E-A6A3-472A-BC13-13F0F12876CF - Created with sketchtool. - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/e2e-verified.svg b/res/img/e2e-verified.svg deleted file mode 100644 index b65f50b2b6..0000000000 --- a/res/img/e2e-verified.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - 48BF5D32-306C-4B20-88EB-24B1F743CAC9 - Created with sketchtool. - - - - - - - \ No newline at end of file diff --git a/res/img/e2e-warning.svg b/res/img/e2e-warning.svg deleted file mode 100644 index 8a55f199ba..0000000000 --- a/res/img/e2e-warning.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - CCDDE6F6-B552-48FD-AD54-6939841CA2DD - Created with sketchtool. - - - - - - - \ No newline at end of file diff --git a/res/img/feather-icons/e2e/blacklisted.svg b/res/img/e2e/blacklisted.svg similarity index 75% rename from res/img/feather-icons/e2e/blacklisted.svg rename to res/img/e2e/blacklisted.svg index 63897c2227..ac99d23f05 100644 --- a/res/img/feather-icons/e2e/blacklisted.svg +++ b/res/img/e2e/blacklisted.svg @@ -2,5 +2,5 @@ - + diff --git a/res/img/feather-icons/e2e/lock-verified.svg b/res/img/e2e/lock-verified.svg similarity index 85% rename from res/img/feather-icons/e2e/lock-verified.svg rename to res/img/e2e/lock-verified.svg index 4cd4684ea2..819dfacc49 100644 --- a/res/img/feather-icons/e2e/lock-verified.svg +++ b/res/img/e2e/lock-verified.svg @@ -1,6 +1,6 @@ - + diff --git a/res/img/e2e/lock-warning-filled.svg b/res/img/e2e/lock-warning-filled.svg new file mode 100644 index 0000000000..a984ed85a0 --- /dev/null +++ b/res/img/e2e/lock-warning-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/e2e/lock-warning.svg b/res/img/e2e/lock-warning.svg index a984ed85a0..de2bded7f8 100644 --- a/res/img/e2e/lock-warning.svg +++ b/res/img/e2e/lock-warning.svg @@ -1 +1,9 @@ - \ No newline at end of file + + + + + + + + + diff --git a/res/img/feather-icons/e2e/verified.svg b/res/img/e2e/verified.svg similarity index 55% rename from res/img/feather-icons/e2e/verified.svg rename to res/img/e2e/verified.svg index f143f854e6..459a552a40 100644 --- a/res/img/feather-icons/e2e/verified.svg +++ b/res/img/e2e/verified.svg @@ -1,3 +1,3 @@ - + diff --git a/res/img/feather-icons/e2e/warning.svg b/res/img/e2e/warning.svg similarity index 78% rename from res/img/feather-icons/e2e/warning.svg rename to res/img/e2e/warning.svg index e6c246dba9..3d5fba550c 100644 --- a/res/img/feather-icons/e2e/warning.svg +++ b/res/img/e2e/warning.svg @@ -2,5 +2,5 @@ - + diff --git a/res/img/edit.svg b/res/img/edit.svg index 9ba0060774..9674b31690 100644 --- a/res/img/edit.svg +++ b/res/img/edit.svg @@ -1,11 +1 @@ - - - - - - - - - + diff --git a/res/img/edit_green.svg b/res/img/edit_green.svg deleted file mode 100644 index f7f4c7adcb..0000000000 --- a/res/img/edit_green.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - diff --git a/res/img/eol.svg b/res/img/eol.svg deleted file mode 100644 index 02d1946cf4..0000000000 --- a/res/img/eol.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - icon_eol - Created with sketchtool. - - - - - - - - - - - diff --git a/res/img/external-link.svg b/res/img/external-link.svg new file mode 100644 index 0000000000..459e790fe3 --- /dev/null +++ b/res/img/external-link.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/img/f4c371.png b/res/img/f4c371.png deleted file mode 100644 index ad3b8f1616..0000000000 Binary files a/res/img/f4c371.png and /dev/null differ diff --git a/res/img/feather-icons/cancel.svg b/res/img/feather-customised/cancel.svg similarity index 100% rename from res/img/feather-icons/cancel.svg rename to res/img/feather-customised/cancel.svg diff --git a/res/img/feather-customised/check.svg b/res/img/feather-customised/check.svg new file mode 100644 index 0000000000..5c600f8649 --- /dev/null +++ b/res/img/feather-customised/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/feather-icons/dropdown-arrow.svg b/res/img/feather-customised/dropdown-arrow.svg similarity index 100% rename from res/img/feather-icons/dropdown-arrow.svg rename to res/img/feather-customised/dropdown-arrow.svg diff --git a/res/img/feather-icons/face.svg b/res/img/feather-customised/face.svg similarity index 67% rename from res/img/feather-icons/face.svg rename to res/img/feather-customised/face.svg index 0a359b2dea..a8ca856b67 100644 --- a/res/img/feather-icons/face.svg +++ b/res/img/feather-customised/face.svg @@ -3,9 +3,9 @@ - - - + + + diff --git a/res/img/feather-icons/files.svg b/res/img/feather-customised/files.svg similarity index 94% rename from res/img/feather-icons/files.svg rename to res/img/feather-customised/files.svg index c66d9ad121..e3bfe30ab0 100644 --- a/res/img/feather-icons/files.svg +++ b/res/img/feather-customised/files.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/flag.svg b/res/img/feather-customised/flag.svg similarity index 100% rename from res/img/feather-icons/flag.svg rename to res/img/feather-customised/flag.svg diff --git a/res/img/feather-icons/flair.svg b/res/img/feather-customised/flair.svg similarity index 100% rename from res/img/feather-icons/flair.svg rename to res/img/feather-customised/flair.svg diff --git a/res/img/feather-icons/globe.svg b/res/img/feather-customised/globe.svg similarity index 100% rename from res/img/feather-icons/globe.svg rename to res/img/feather-customised/globe.svg diff --git a/res/img/feather-icons/grid.svg b/res/img/feather-customised/grid.svg similarity index 95% rename from res/img/feather-icons/grid.svg rename to res/img/feather-customised/grid.svg index e6912b0cc7..4f7ab30d97 100644 --- a/res/img/feather-icons/grid.svg +++ b/res/img/feather-customised/grid.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/help-circle.svg b/res/img/feather-customised/help-circle.svg similarity index 100% rename from res/img/feather-icons/help-circle.svg rename to res/img/feather-customised/help-circle.svg diff --git a/res/img/feather-customised/home.svg b/res/img/feather-customised/home.svg new file mode 100644 index 0000000000..7bb31b23dc --- /dev/null +++ b/res/img/feather-customised/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/feather-icons/life-buoy.svg b/res/img/feather-customised/life-buoy.svg similarity index 95% rename from res/img/feather-icons/life-buoy.svg rename to res/img/feather-customised/life-buoy.svg index 20bd0f0b5d..6318f7df9a 100644 --- a/res/img/feather-icons/life-buoy.svg +++ b/res/img/feather-customised/life-buoy.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/lock.svg b/res/img/feather-customised/lock.svg similarity index 100% rename from res/img/feather-icons/lock.svg rename to res/img/feather-customised/lock.svg diff --git a/res/img/feather-icons/notifications.svg b/res/img/feather-customised/notifications.svg similarity index 94% rename from res/img/feather-icons/notifications.svg rename to res/img/feather-customised/notifications.svg index 2fe85e810c..a590031ac3 100644 --- a/res/img/feather-icons/notifications.svg +++ b/res/img/feather-customised/notifications.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/paperclip.svg b/res/img/feather-customised/paperclip.svg similarity index 95% rename from res/img/feather-icons/paperclip.svg rename to res/img/feather-customised/paperclip.svg index ed2bb88681..74a90e0fa3 100644 --- a/res/img/feather-icons/paperclip.svg +++ b/res/img/feather-customised/paperclip.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/phone.svg b/res/img/feather-customised/phone.svg similarity index 96% rename from res/img/feather-icons/phone.svg rename to res/img/feather-customised/phone.svg index 58b257f113..85661c5320 100644 --- a/res/img/feather-icons/phone.svg +++ b/res/img/feather-customised/phone.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/search-input.svg b/res/img/feather-customised/search-input.svg similarity index 93% rename from res/img/feather-icons/search-input.svg rename to res/img/feather-customised/search-input.svg index 3be5acb32e..028b84d559 100644 --- a/res/img/feather-icons/search-input.svg +++ b/res/img/feather-customised/search-input.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/search.svg b/res/img/feather-customised/search.svg similarity index 93% rename from res/img/feather-icons/search.svg rename to res/img/feather-customised/search.svg index 8b14246f64..9ce0724ea7 100644 --- a/res/img/feather-icons/search.svg +++ b/res/img/feather-customised/search.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/settings.svg b/res/img/feather-customised/settings.svg similarity index 98% rename from res/img/feather-icons/settings.svg rename to res/img/feather-customised/settings.svg index ea7ce5c55b..a65a15a75f 100644 --- a/res/img/feather-icons/settings.svg +++ b/res/img/feather-customised/settings.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/share.svg b/res/img/feather-customised/share.svg similarity index 95% rename from res/img/feather-icons/share.svg rename to res/img/feather-customised/share.svg index a012e1b7a5..7098af58aa 100644 --- a/res/img/feather-icons/share.svg +++ b/res/img/feather-customised/share.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-customised/sign-in.svg b/res/img/feather-customised/sign-in.svg new file mode 100644 index 0000000000..9fe617eee1 --- /dev/null +++ b/res/img/feather-customised/sign-in.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/res/img/feather-icons/sign-out.svg b/res/img/feather-customised/sign-out.svg similarity index 83% rename from res/img/feather-icons/sign-out.svg rename to res/img/feather-customised/sign-out.svg index 06ab9903ec..172eb98afb 100644 --- a/res/img/feather-icons/sign-out.svg +++ b/res/img/feather-customised/sign-out.svg @@ -1,5 +1,5 @@ - + diff --git a/res/img/feather-icons/sliders.svg b/res/img/feather-customised/sliders.svg similarity index 100% rename from res/img/feather-icons/sliders.svg rename to res/img/feather-customised/sliders.svg diff --git a/res/img/feather-icons/upload.svg b/res/img/feather-customised/upload.svg similarity index 83% rename from res/img/feather-icons/upload.svg rename to res/img/feather-customised/upload.svg index 8ec5d95c2c..30c89d3819 100644 --- a/res/img/feather-icons/upload.svg +++ b/res/img/feather-customised/upload.svg @@ -1,5 +1,5 @@ - + diff --git a/res/img/feather-icons/user-add.svg b/res/img/feather-customised/user-add.svg similarity index 95% rename from res/img/feather-icons/user-add.svg rename to res/img/feather-customised/user-add.svg index cbb25934c1..6b5210c1d6 100644 --- a/res/img/feather-icons/user-add.svg +++ b/res/img/feather-customised/user-add.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/user.svg b/res/img/feather-customised/user.svg similarity index 94% rename from res/img/feather-icons/user.svg rename to res/img/feather-customised/user.svg index a789e580d5..210ef99e6a 100644 --- a/res/img/feather-icons/user.svg +++ b/res/img/feather-customised/user.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/users-sm.svg b/res/img/feather-customised/users-sm.svg similarity index 100% rename from res/img/feather-icons/users-sm.svg rename to res/img/feather-customised/users-sm.svg diff --git a/res/img/feather-icons/users.svg b/res/img/feather-customised/users.svg similarity index 96% rename from res/img/feather-icons/users.svg rename to res/img/feather-customised/users.svg index b0deac0a9e..b90aafdd4a 100644 --- a/res/img/feather-icons/users.svg +++ b/res/img/feather-customised/users.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-icons/video.svg b/res/img/feather-customised/video.svg similarity index 93% rename from res/img/feather-icons/video.svg rename to res/img/feather-customised/video.svg index a4c456832f..da77b6c57a 100644 --- a/res/img/feather-icons/video.svg +++ b/res/img/feather-customised/video.svg @@ -1,7 +1,7 @@ - + diff --git a/res/img/feather-customised/warning-triangle.svg b/res/img/feather-customised/warning-triangle.svg new file mode 100644 index 0000000000..3d18e30fb2 --- /dev/null +++ b/res/img/feather-customised/warning-triangle.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/res/img/feather-icons/widget/bin.svg b/res/img/feather-customised/widget/bin.svg similarity index 100% rename from res/img/feather-icons/widget/bin.svg rename to res/img/feather-customised/widget/bin.svg diff --git a/res/img/feather-icons/widget/camera.svg b/res/img/feather-customised/widget/camera.svg similarity index 100% rename from res/img/feather-icons/widget/camera.svg rename to res/img/feather-customised/widget/camera.svg diff --git a/res/img/feather-icons/widget/edit.svg b/res/img/feather-customised/widget/edit.svg similarity index 100% rename from res/img/feather-icons/widget/edit.svg rename to res/img/feather-customised/widget/edit.svg diff --git a/res/img/feather-icons/widget/external-link.svg b/res/img/feather-customised/widget/external-link.svg similarity index 100% rename from res/img/feather-icons/widget/external-link.svg rename to res/img/feather-customised/widget/external-link.svg diff --git a/res/img/feather-icons/widget/maximise.svg b/res/img/feather-customised/widget/maximise.svg similarity index 100% rename from res/img/feather-icons/widget/maximise.svg rename to res/img/feather-customised/widget/maximise.svg diff --git a/res/img/feather-icons/widget/minimise.svg b/res/img/feather-customised/widget/minimise.svg similarity index 100% rename from res/img/feather-icons/widget/minimise.svg rename to res/img/feather-customised/widget/minimise.svg diff --git a/res/img/feather-icons/widget/refresh.svg b/res/img/feather-customised/widget/refresh.svg similarity index 100% rename from res/img/feather-icons/widget/refresh.svg rename to res/img/feather-customised/widget/refresh.svg diff --git a/res/img/feather-icons/widget/x-circle.svg b/res/img/feather-customised/widget/x-circle.svg similarity index 100% rename from res/img/feather-icons/widget/x-circle.svg rename to res/img/feather-customised/widget/x-circle.svg diff --git a/res/img/feather-customised/x.svg b/res/img/feather-customised/x.svg new file mode 100644 index 0000000000..5468caa8aa --- /dev/null +++ b/res/img/feather-customised/x.svg @@ -0,0 +1,4 @@ + + + + diff --git a/res/img/feather-icons/e2e/lock-warning.svg b/res/img/feather-icons/e2e/lock-warning.svg deleted file mode 100644 index 507c532f9d..0000000000 --- a/res/img/feather-icons/e2e/lock-warning.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/res/img/feather-icons/warning-triangle.svg b/res/img/feather-icons/warning-triangle.svg deleted file mode 100644 index 02196cbf43..0000000000 --- a/res/img/feather-icons/warning-triangle.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/res/img/icon-call.svg b/res/img/icon-call.svg deleted file mode 100644 index 98677e3c70..0000000000 --- a/res/img/icon-call.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icon-context-delete.svg b/res/img/icon-context-delete.svg deleted file mode 100644 index fba9fa117b..0000000000 --- a/res/img/icon-context-delete.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/res/img/icon-context-fave-on.svg b/res/img/icon-context-fave-on.svg deleted file mode 100644 index 2ae172d8eb..0000000000 --- a/res/img/icon-context-fave-on.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - DAE17B64-40B5-478A-8E8D-97AD1A6E25C8 - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon-context-fave.svg b/res/img/icon-context-fave.svg deleted file mode 100644 index 451e1849c8..0000000000 --- a/res/img/icon-context-fave.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - 8A6E1837-F0F1-432E-A0DA-6F3741F71EBF - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon-context-low-on.svg b/res/img/icon-context-low-on.svg deleted file mode 100644 index 7578c6335c..0000000000 --- a/res/img/icon-context-low-on.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - CD51482C-F2D4-4F63-AF9E-86513F9AF87F - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon-context-low.svg b/res/img/icon-context-low.svg deleted file mode 100644 index 663f3ca9eb..0000000000 --- a/res/img/icon-context-low.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - B160345F-40D3-4BE6-A860-6D04BF223EF7 - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon-delete-pink.svg b/res/img/icon-delete-pink.svg deleted file mode 100644 index aafa87f1b2..0000000000 --- a/res/img/icon-delete-pink.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - diff --git a/res/img/icon-mx-user.svg b/res/img/icon-mx-user.svg deleted file mode 100644 index 5780277f38..0000000000 --- a/res/img/icon-mx-user.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/res/img/icon_context_copy.svg b/res/img/icon_context_copy.svg deleted file mode 100644 index 1f9c0b01e8..0000000000 --- a/res/img/icon_context_copy.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icon_context_edit.svg b/res/img/icon_context_edit.svg deleted file mode 100644 index 6f7f1fd385..0000000000 --- a/res/img/icon_context_edit.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icon_context_leave.svg b/res/img/icon_context_leave.svg deleted file mode 100644 index 3fdd452a59..0000000000 --- a/res/img/icon_context_leave.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/res/img/icon_context_message.svg b/res/img/icon_context_message.svg deleted file mode 100644 index f2ceccfa78..0000000000 --- a/res/img/icon_context_message.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon_context_message_dark.svg b/res/img/icon_context_message_dark.svg deleted file mode 100644 index b4336cc377..0000000000 --- a/res/img/icon_context_message_dark.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icon_context_reply.svg b/res/img/icon_context_reply.svg deleted file mode 100644 index 0a85f8e606..0000000000 --- a/res/img/icon_context_reply.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icon_copy_message_dark.svg b/res/img/icon_copy_message_dark.svg deleted file mode 100644 index b81e617d8c..0000000000 --- a/res/img/icon_copy_message_dark.svg +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - image/svg+xml - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - - - - - - ED5D3E59-2561-4AC1-9B43-82FBC51767FC - Created with sketchtool. - - - - - - - diff --git a/res/img/icons-apps-active.svg b/res/img/icons-apps-active.svg deleted file mode 100644 index ea222d0511..0000000000 --- a/res/img/icons-apps-active.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/res/img/icons-apps.svg b/res/img/icons-apps.svg deleted file mode 100644 index e2fe49b005..0000000000 --- a/res/img/icons-apps.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/res/img/icons-close-button.svg b/res/img/icons-close-button.svg deleted file mode 100644 index f960d73a3c..0000000000 --- a/res/img/icons-close-button.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - 206C270A-EB00-48E4-8CC3-5D403C59177C - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icons-files.svg b/res/img/icons-files.svg deleted file mode 100644 index ea270fbc73..0000000000 --- a/res/img/icons-files.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/res/img/icons-groups-nobg.svg b/res/img/icons-groups-nobg.svg deleted file mode 100644 index a3d223b76d..0000000000 --- a/res/img/icons-groups-nobg.svg +++ /dev/null @@ -1,60 +0,0 @@ - -image/svg+xml - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/icons-hide-apps.svg b/res/img/icons-hide-apps.svg deleted file mode 100644 index b622e97f71..0000000000 --- a/res/img/icons-hide-apps.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/img/icons-hide-stickers.svg b/res/img/icons-hide-stickers.svg deleted file mode 100644 index f28e8646e6..0000000000 --- a/res/img/icons-hide-stickers.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - diff --git a/res/img/icons-home.svg b/res/img/icons-home.svg deleted file mode 100644 index eb5484c883..0000000000 --- a/res/img/icons-home.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - 81230A28-D944-4572-B5DB-C03CAA2B1FCA - Created with sketchtool. - - - - - - - - - - - - - - - - - diff --git a/res/img/icons-notifications.svg b/res/img/icons-notifications.svg deleted file mode 100644 index cde30713e1..0000000000 --- a/res/img/icons-notifications.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icons-room.svg b/res/img/icons-room.svg deleted file mode 100644 index d2abb21301..0000000000 --- a/res/img/icons-room.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/res/img/icons-search.svg b/res/img/icons-search.svg deleted file mode 100644 index 9d3e98106b..0000000000 --- a/res/img/icons-search.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - Shape - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/res/img/icons-settings.svg b/res/img/icons-settings.svg deleted file mode 100644 index 3ca2b655f4..0000000000 --- a/res/img/icons-settings.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - 4D42A2A7-7430-4D4F-A0A2-E19278CF66E3 - Created with sketchtool. - - - - - - - - - - diff --git a/res/img/icons-show-apps.svg b/res/img/icons-show-apps.svg deleted file mode 100644 index 3438157301..0000000000 --- a/res/img/icons-show-apps.svg +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/img/icons-stickers.svg b/res/img/icons-stickers.svg deleted file mode 100644 index 564ebdac97..0000000000 --- a/res/img/icons-stickers.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/res/img/icons-upload.svg b/res/img/icons-upload.svg deleted file mode 100644 index 3aea924478..0000000000 --- a/res/img/icons-upload.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icons-video.svg b/res/img/icons-video.svg deleted file mode 100644 index c61a782cc4..0000000000 --- a/res/img/icons-video.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/res/img/icons_ellipsis.svg b/res/img/icons_ellipsis.svg deleted file mode 100644 index ba600ccacc..0000000000 --- a/res/img/icons_ellipsis.svg +++ /dev/null @@ -1 +0,0 @@ - diff --git a/res/img/icons_global.svg b/res/img/icons_global.svg deleted file mode 100644 index 6c07d3c48e..0000000000 --- a/res/img/icons_global.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - icons_global copy 4 - Created with Sketch. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/list-close.svg b/res/img/list-close.svg deleted file mode 100644 index cd88b2a88f..0000000000 --- a/res/img/list-close.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - -Slice 1 -Created with Sketch. - - - - diff --git a/res/img/list-open.svg b/res/img/list-open.svg deleted file mode 100644 index e180be8870..0000000000 --- a/res/img/list-open.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - -Slice 1 -Created with Sketch. - - - - diff --git a/res/img/markdown.svg b/res/img/markdown.svg new file mode 100644 index 0000000000..9aadd3cb7f --- /dev/null +++ b/res/img/markdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/res/img/feather-icons/matrix-org-bw-logo.svg b/res/img/matrix-org-bw-logo.svg similarity index 100% rename from res/img/feather-icons/matrix-org-bw-logo.svg rename to res/img/matrix-org-bw-logo.svg diff --git a/res/img/maximise.svg b/res/img/maximise.svg deleted file mode 100644 index 981e3796de..0000000000 --- a/res/img/maximise.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - -minimise -Created with sketchtool. - - - - - - - - - - - - diff --git a/res/img/maximize.svg b/res/img/maximize.svg deleted file mode 100644 index 4f9e10191f..0000000000 --- a/res/img/maximize.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/res/img/minimize.svg b/res/img/minimize.svg deleted file mode 100644 index 410b0bc08e..0000000000 --- a/res/img/minimize.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/res/img/feather-icons/modular-bw-logo.svg b/res/img/modular-bw-logo.svg similarity index 100% rename from res/img/feather-icons/modular-bw-logo.svg rename to res/img/modular-bw-logo.svg diff --git a/res/img/newmessages.svg b/res/img/newmessages.svg deleted file mode 100644 index a2ffca9020..0000000000 --- a/res/img/newmessages.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - icon_newmessages - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/res/img/reply.svg b/res/img/reply.svg new file mode 100644 index 0000000000..540e228883 --- /dev/null +++ b/res/img/reply.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/res/img/right_search.svg b/res/img/right_search.svg deleted file mode 100644 index b430a6be19..0000000000 --- a/res/img/right_search.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - right_search - Created with Sketch. - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/rotate-ccw.svg b/res/img/rotate-ccw.svg new file mode 100644 index 0000000000..3924eca040 --- /dev/null +++ b/res/img/rotate-ccw.svg @@ -0,0 +1 @@ + diff --git a/res/img/rotate-cw.svg b/res/img/rotate-cw.svg new file mode 100644 index 0000000000..91021c96d8 --- /dev/null +++ b/res/img/rotate-cw.svg @@ -0,0 +1 @@ + diff --git a/res/img/scrolldown.svg b/res/img/scrolldown.svg deleted file mode 100644 index d6599c5fc7..0000000000 --- a/res/img/scrolldown.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - icon_newmessages - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/res/img/scrollto.svg b/res/img/scrollto.svg deleted file mode 100644 index 75df053a68..0000000000 --- a/res/img/scrollto.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - Slice 1 - Created with Sketch. - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/scrollup.svg b/res/img/scrollup.svg deleted file mode 100644 index 1692f2a6c0..0000000000 --- a/res/img/scrollup.svg +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - image/svg+xml - - - - - - - icon_newmessages - Created with Sketch. - - - - - - - - - - diff --git a/res/img/search-button.svg b/res/img/search-button.svg deleted file mode 100644 index f4808842ff..0000000000 --- a/res/img/search-button.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - icon_search - Created with Sketch. - - - - - - - - - - \ No newline at end of file diff --git a/res/img/settings-big.svg b/res/img/settings-big.svg deleted file mode 100644 index c9587d58c2..0000000000 --- a/res/img/settings-big.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - icons_settings - Created with sketchtool. - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/settings.svg b/res/img/settings.svg deleted file mode 100644 index 4190c7b8de..0000000000 --- a/res/img/settings.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - icon_settings_small - Created with bin/sketchtool. - - - - - - - \ No newline at end of file diff --git a/res/img/upload.svg b/res/img/upload.svg deleted file mode 100644 index 039014a2f3..0000000000 --- a/res/img/upload.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - icons_upload - Created with bin/sketchtool. - - - - - - - - - - - - - - \ No newline at end of file diff --git a/res/img/voice.svg b/res/img/voice.svg deleted file mode 100644 index ff87270ba5..0000000000 --- a/res/img/voice.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - icon_voice - Created with Sketch. - - - - - - - - \ No newline at end of file diff --git a/res/img/warning_yellow.svg b/res/img/warning_yellow.svg deleted file mode 100644 index 4d227517d2..0000000000 --- a/res/img/warning_yellow.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/res/themes/dark/css/_dark.scss b/res/themes/dark/css/_dark.scss index ee5e1cf5d6..bdccf71540 100644 --- a/res/themes/dark/css/_dark.scss +++ b/res/themes/dark/css/_dark.scss @@ -1,186 +1,176 @@ +// unified palette +// try to use these colors when possible +$bg-color: #181b21; +$base-color: #15171b; +$base-text-color: #edf3ff; +$header-panel-bg-color: #22262e; +$header-panel-border-color: #000000; +$header-panel-text-primary-color: #a1b2d1; +$header-panel-text-secondary-color: #c8c8cd; +$text-primary-color: #edf3ff; +$text-secondary-color: #a1b2d1; +$search-bg-color: #181b21; +$search-placeholder-color: #61708b; +$room-highlight-color: #343a46; // typical text (dark-on-white in light skin) -$primary-fg-color: #212121; -$primary-bg-color: #2d2d2d; - -// used for focusing form controls -$focus-bg-color: #101010; +$primary-fg-color: $text-primary-color; +$primary-bg-color: $bg-color; // used for dialog box text -$light-fg-color: #747474; +$light-fg-color: $header-panel-text-secondary-color; -// button UI (white-on-green in light skin) -$accent-fg-color: $primary-bg-color; -$accent-color: #76CFA6; -$accent-color-alt: $accent-color; -$accent-color-50pct: #76CFA67F; +// used for focusing form controls +$focus-bg-color: $room-highlight-color; -$selection-fg-color: $primary-fg-color; +$mention-user-pill-bg-color: $warning-color; +$other-user-pill-bg-color: $room-highlight-color; +$rte-room-pill-color: $room-highlight-color; +$rte-group-pill-color: $room-highlight-color; -$focus-brightness: 200%; +// informational plinth +$info-plinth-bg-color: $header-panel-bg-color; +$info-plinth-fg-color: #888; -// red warning colour -$warning-color: #ff0064; -$warning-bg-color: #DF2A8B; -$info-bg-color: #2A9EDF; +$preview-bar-bg-color: $header-panel-bg-color; -// groups -$info-plinth-bg-color: #454545; - -$other-user-pill-bg-color: rgba(255, 255, 255, 0.1); - -$preview-bar-bg-color: #333; - -// left-panel style muted accent color -$secondary-accent-color: $primary-bg-color; -$tertiary-accent-color: #454545; - -// stop the tinter trying to change the secondary accent color -// by overriding the key to something untintable -// XXX: this is a bit of a hack. -#mx_theme_secondaryAccentColor { - color: #c0ffee ! important; -} - -#mx_theme_tertiaryAccentColor { - color: #c0ffee ! important; -} - -// used by RoomDirectory permissions -$plinth-bg-color: #474747; - -// used by RoomDropTarget -$droptarget-bg-color: rgba(45,45,45,0.5); +$tagpanel-bg-color: $base-color; // used by AddressSelector -$selected-color: #000000; +$selected-color: $room-highlight-color; // selected for hoverover & selected event tiles -$event-selected-color: #353535; +$event-selected-color: $header-panel-bg-color; // used for the hairline dividers in RoomView -$primary-hairline-color: #474747; +$primary-hairline-color: $header-panel-border-color; // used for the border of input text fields -$input-border-color: #3a3a3a; +$input-border-color: #e7e7e7; +$input-darker-bg-color: $search-bg-color; +$input-darker-fg-color: $search-placeholder-color; +$input-lighter-bg-color: #f2f5f8; +$input-lighter-fg-color: $input-darker-fg-color; +$input-focused-border-color: #238cf5; +$input-valid-border-color: $accent-color; +$input-invalid-border-color: $warning-color; -$input-darker-bg-color: #c1c9d6; -$input-darker-fg-color: #9fa9ba; -$button-bg-color: #7ac9a1; -$button-fg-color: white; -// apart from login forms, which have stronger border -$strong-input-border-color: #656565; +$field-focused-label-bg-color: $bg-color; -// used for UserSettings EditableText -$input-underline-color: $primary-fg-color; -$input-fg-color: $primary-fg-color; // scrollbars $scrollbar-thumb-color: rgba(255, 255, 255, 0.2); $scrollbar-track-color: transparent; -// context menus -$menu-border-color: rgba(187, 187, 187, 0.5); -$menu-bg-color: #373737; -$menu-selected-color: #f5f8fa; -$avatar-initial-color: #2d2d2d; -$avatar-bg-color: #ffffff; -$menu-selected-color: #f5f8fa; +// context menus +$menu-border-color: $header-panel-border-color; +$menu-bg-color: $header-panel-bg-color; +$menu-box-shadow-color: $bg-color; +$menu-selected-color: $room-highlight-color; + +$avatar-initial-color: #ffffff; +$avatar-bg-color: $bg-color; $h3-color: $primary-fg-color; -$dialog-background-bg-color: #000; +$dialog-title-fg-color: $base-text-color; +$dialog-backdrop-color: #000; +$dialog-shadow-color: rgba(0, 0, 0, 0.48); +$dialog-close-fg-color: #9fa9ba; + +$dialog-background-bg-color: $header-panel-bg-color; $lightbox-background-bg-color: #000; -$greyed-fg-color: #888; - -$neutral-badge-color: #888; - -$preview-widget-bar-color: $menu-bg-color; -$preview-widget-fg-color: $greyed-fg-color; - -$blockquote-bar-color: #ddd; -$blockquote-fg-color: #777; - $settings-grey-fg-color: #a2a2a2; +$settings-profile-placeholder-bg-color: #e7e7e7; +$settings-profile-overlay-bg-color: #000; +$settings-profile-overlay-placeholder-bg-color: transparent; +$settings-profile-overlay-fg-color: #fff; +$settings-profile-overlay-placeholder-fg-color: #454545; +$settings-subsection-fg-color: $text-secondary-color; -$voip-decline-color: #f48080; -$voip-accept-color: #80f480; +$topleftmenu-color: $text-primary-color; +$roomheader-color: $text-primary-color; +$roomheader-addroom-bg-color: #3c4556; // $search-placeholder-color at 0.5 opacity +$roomheader-addroom-fg-color: $text-primary-color; +$tagpanel-button-color: $header-panel-text-primary-color; +$roomheader-button-color: $header-panel-text-primary-color; +$groupheader-button-color: $header-panel-text-primary-color; +$rightpanel-button-color: $header-panel-text-primary-color; +$composer-button-color: $header-panel-text-primary-color; +$roomtopic-color: $text-secondary-color; +$eventtile-meta-color: $roomtopic-color; -$rte-bg-color: #353535; -$rte-code-bg-color: #000; +$header-divider-color: $header-panel-text-primary-color; -$room-warning-bg-color: #2d2d2d; +$roomtile-name-color: $header-panel-text-primary-color; +$roomtile-selected-color: $text-primary-color; +$roomtile-notified-color: $text-primary-color; +$roomtile-selected-bg-color: $room-highlight-color; +$roomtile-focused-bg-color: $room-highlight-color; -// ******************** +$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); -$roomtile-name-color: rgba(186, 186, 186, 0.8); -$roomtile-selected-bg-color: #333; -$roomtile-focused-bg-color: rgba(255, 255, 255, 0.2); +$panel-divider-color: $header-panel-border-color; -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; - -$roomsublist-background: rgba(0, 0, 0, 0.2); -$roomsublist-label-fg-color: $h3-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - -$panel-divider-color: rgba(118, 207, 166, 0.2); - -// ******************** - -$widget-menu-bar-bg-color: $tertiary-accent-color; - -// ******************** +$widget-menu-bar-bg-color: $header-panel-bg-color; // event tile lifecycle -$event-encrypting-color: rgba(171, 221, 188, 0.4); -$event-sending-color: #888; -$event-notsent-color: #f44; +$event-sending-color: $text-secondary-color; // event redaction $event-redacted-fg-color: #606060; $event-redacted-border-color: #000000; // event timestamp -$event-timestamp-color: #acacac; +$event-timestamp-color: $text-secondary-color; -$edit-button-url: "$(res)/img/icon_context_message_dark.svg"; -$copy-button-url: "$(res)/img/icon_copy_message_dark.svg"; +// Tabbed views +$tab-label-fg-color: $text-primary-color; +$tab-label-active-fg-color: $text-primary-color; +$tab-label-bg-color: transparent; +$tab-label-active-bg-color: $accent-color; +$tab-label-icon-bg-color: $text-primary-color; +$tab-label-active-icon-bg-color: $text-primary-color; -// e2e -$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color -$e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; +// Buttons +$button-primary-fg-color: #ffffff; +$button-primary-bg-color: $accent-color; +$button-secondary-bg-color: transparent; +$button-danger-fg-color: #ffffff; +$button-danger-bg-color: $notice-primary-color; +$button-danger-disabled-fg-color: #ffffff; +$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color -/*** ImageView ***/ -$lightbox-bg-color: #454545; -$lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; +$room-warning-bg-color: $header-panel-bg-color; -$imagebody-giflabel: rgba(1, 1, 1, 0.7); -$imagebody-giflabel-border: rgba(1, 1, 1, 0.2); -$imagebody-giflabel-color: rgba(0, 0, 0, 1); +$dark-panel-bg-color: $header-panel-bg-color; +$panel-gradient: rgba(34, 38, 46, 0), rgba(34, 38, 46, 1); -// unused? -$progressbar-color: #000; +$message-action-bar-bg-color: $header-panel-bg-color; +$message-action-bar-fg-color: $header-panel-text-primary-color; +$message-action-bar-border-color: #616b7f; +$message-action-bar-hover-border-color: $header-panel-text-primary-color; + +$reaction-row-button-bg-color: $header-panel-bg-color; +$reaction-row-button-border-color: #616b7f; +$reaction-row-button-hover-border-color: $header-panel-text-primary-color; +$reaction-row-button-selected-bg-color: #1f6954; +$reaction-row-button-selected-border-color: $accent-color; + +$tooltip-timeline-bg-color: $tagpanel-bg-color; +$tooltip-timeline-fg-color: #ffffff; + +// ***** Mixins! ***** -// XXX: copypasted from _base in order to pick up the right FG color... @define-mixin mx_DialogButton { /* align images in buttons (eg spinners) */ vertical-align: middle; border: 0px; - border-radius: 36px; + border-radius: 4px; font-family: $font-family; font-size: 14px; - color: $accent-fg-color; - background-color: $accent-color; + color: $button-fg-color; + background-color: $button-bg-color; width: auto; padding: 7px; padding-left: 1.5em; @@ -190,12 +180,16 @@ $progressbar-color: #000; outline: none; } +@define-mixin mx_DialogButton_danger { + background-color: $accent-color; +} + @define-mixin mx_DialogButton_secondary { // flip colours for the secondary ones font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; } // Nasty hacks to apply a filter to arbitrary monochrome artwork to make it @@ -232,8 +226,3 @@ $progressbar-color: #000; } } } - -// Add a line to the right side of the left panel to distinguish it from the middle panel -.mx_LeftPanel { - border-right: 1px solid $tertiary-accent-color; -} diff --git a/res/themes/dark/css/dark.scss b/res/themes/dark/css/dark.scss index a7d18fa1a6..e7ae7c8cf8 100644 --- a/res/themes/dark/css/dark.scss +++ b/res/themes/dark/css/dark.scss @@ -1,5 +1,5 @@ @import "../../light/css/_paths.scss"; @import "../../light/css/_fonts.scss"; -@import "../../light/css/_base.scss"; +@import "../../light/css/_light.scss"; @import "_dark.scss"; @import "../../../../res/css/_components.scss"; diff --git a/res/themes/dharma/css/_fonts.scss b/res/themes/dharma/css/_fonts.scss deleted file mode 100644 index ac15847e44..0000000000 --- a/res/themes/dharma/css/_fonts.scss +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Nunito. - * Includes extended Latin and Vietnamese character sets - * Current URLs are taken from - * https://github.com/alexeiva/NunitoFont/releases/tag/v3.500 - * ...in order to include cyrillic. - * - * Previously, they were - * https://fonts.googleapis.com/css?family=Nunito:400,400i,600,600i,700,700i&subset=latin-ext,vietnamese - * - * We explicitly do not include Nunito's italic variants, as they are not italic enough - * and it's better to rely on the browser's built-in obliquing behaviour. - */ - -/* the 'src' links are relative to the bundle.css, which is in a subdirectory. - */ -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 400; - src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); -} -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 600; - src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); -} -@font-face { - font-family: 'Nunito'; - font-style: normal; - font-weight: 700; - src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); -} - -/* - * Fira Mono - * Used for monospace copy, i.e. code - */ - -@font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); - font-weight: 700; - font-style: normal; -} diff --git a/res/themes/dharma/css/dharma.scss b/res/themes/dharma/css/dharma.scss deleted file mode 100644 index 24dc0ce18d..0000000000 --- a/res/themes/dharma/css/dharma.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import "../../light/css/_paths.scss"; -@import "_fonts.scss"; -@import "_dharma.scss"; -@import "../../../../res/css/_components.scss"; diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss deleted file mode 100644 index 8185ba0ace..0000000000 --- a/res/themes/light/css/_base.scss +++ /dev/null @@ -1,276 +0,0 @@ -/* Open Sans lacks combining diacritics, so these will fall through - to the next font. Helevetica's diacritics however do not combine - nicely with Open Sans (on OSX, at least) and result in a huge - horizontal mess. Arial empirically gets it right, hence prioritising - Arial here. */ -$font-family: 'Open Sans', Arial, Helvetica, Sans-Serif; - -// typical text (dark-on-white in light skin) -$primary-fg-color: #454545; -$primary-bg-color: #ffffff; - -// used for dialog box text -$light-fg-color: #747474; - -// used for focusing form controls -$focus-bg-color: #dddddd; - -// button UI (white-on-green in light skin) -$accent-fg-color: #ffffff; -$accent-color: #76CFA6; -$accent-color-alt: $accent-color; -$accent-color-50pct: #76CFA67F; - -$selection-fg-color: $primary-bg-color; - -$focus-brightness: 125%; - -// red warning colour -$warning-color: #ff0064; -// background colour for warnings -$warning-bg-color: #DF2A8B; -$info-bg-color: #2A9EDF; -$mention-user-pill-bg-color: #ff0064; -$other-user-pill-bg-color: rgba(0, 0, 0, 0.1); - -// pinned events indicator -$pinned-unread-color: #ff0064; // $warning-color -$pinned-color: #888; - -// informational plinth -$info-plinth-bg-color: #f7f7f7; -$info-plinth-fg-color: #888; - -$preview-bar-bg-color: #f7f7f7; - -// left-panel style muted accent color -$secondary-accent-color: #eaf5f0; -$tertiary-accent-color: #d3efe1; - -$tagpanel-bg-color: $tertiary-accent-color; - -// used by RoomDirectory permissions -$plinth-bg-color: $secondary-accent-color; - -// used by RoomDropTarget -$droptarget-bg-color: rgba(255,255,255,0.5); - -// used by AddressSelector -$selected-color: $secondary-accent-color; - -// selected for hoverover & selected event tiles -$event-selected-color: #f7f7f7; - -// used for the hairline dividers in RoomView -$primary-hairline-color: #e5e5e5; - -// used for the border of input text fields -$input-border-color: #f0f0f0; -$input-border-dark-color: #b8b8b8; - -$input-darker-bg-color: #c1c9d6; -$input-darker-fg-color: #9fa9ba; -$button-bg-color: #7ac9a1; -$button-fg-color: white; -// apart from login forms, which have stronger border -$strong-input-border-color: #c7c7c7; -$input-focused-border-color: #238cf5; -$input-valid-border-color: #7ac9a1; - -$field-focused-label-bg-color: #ffffff; - -// used for UserSettings EditableText -$input-underline-color: rgba(151, 151, 151, 0.5); -$input-fg-color: rgba(74, 74, 74, 0.9); -// scrollbars -$scrollbar-thumb-color: rgba(0, 0, 0, 0.2); -$scrollbar-track-color: transparent; -// context menus -$menu-border-color: rgba(187, 187, 187, 0.5); -$menu-bg-color: #f6f6f6; -$menu-selected-color: #f5f8fa; - -$avatar-initial-color: #ffffff; -$avatar-bg-color: #ffffff; - -$h3-color: #3d3b39; - -$dialog-title-fg-color: #454545; -$dialog-backdrop-color: rgba(46, 48, 51, 0.38); -$dialog-shadow-color: rgba(0, 0, 0, 0.48); -$dialog-close-fg-color: #9fa9ba; - -$dialog-background-bg-color: #e9e9e9; -$lightbox-background-bg-color: #000; - -$greyed-fg-color: #888; - -$neutral-badge-color: #dbdbdb; - -$preview-widget-bar-color: #ddd; -$preview-widget-fg-color: $greyed-fg-color; - -$blockquote-bar-color: #ddd; -$blockquote-fg-color: #777; - -$settings-grey-fg-color: #a2a2a2; -$settings-profile-placeholder-bg-color: #e7e7e7; -$settings-profile-overlay-bg-color: #000; -$settings-profile-overlay-placeholder-bg-color: transparent; -$settings-profile-overlay-fg-color: #fff; -$settings-profile-overlay-placeholder-fg-color: #454545; -$settings-subsection-fg-color: #61708b; - -$voip-decline-color: #f48080; -$voip-accept-color: #80f480; - -$rte-bg-color: #e9e9e9; -$rte-code-bg-color: rgba(0, 0, 0, 0.04); -$rte-room-pill-color: #aaa; -$rte-group-pill-color: #aaa; - -$topleftmenu-color: $primary-fg-color; -$roomheader-color: $primary-fg-color; -$roomheader-addroom-color: $primary-bg-color; -$roomtopic-color: $settings-grey-fg-color; -$eventtile-meta-color: $roomtopic-color; - -$composer-e2e-icon-color: #c9ced6; - -// ******************** - -$roomtile-name-color: rgba(69, 69, 69, 0.8); -$roomtile-selected-color: $roomtile-name-color; -$roomtile-notified-color: $roomtile-name-color; -$roomtile-selected-bg-color: rgba(255, 255, 255, 0.8); -$roomtile-focused-bg-color: rgba(255, 255, 255, 0.9); - -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; - -$roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); - -$roomsublist-background: rgba(0, 0, 0, 0.05); -$roomsublist-label-fg-color: $h3-color; -$roomsublist-label-bg-color: $tertiary-accent-color; -$roomsublist-chevron-color: $accent-color; - -$panel-divider-color: rgba(118, 207, 166, 0.2); - -// ******************** - -$widget-menu-bar-bg-color: $tertiary-accent-color; - -// ******************** - -// event tile lifecycle -$event-encrypting-color: #abddbc; -$event-sending-color: #ddd; -$event-notsent-color: #f44; - -// event redaction -$event-redacted-fg-color: #e2e2e2; -$event-redacted-border-color: #cccccc; - -// event timestamp -$event-timestamp-color: #acacac; - -$edit-button-url: "$(res)/img/icon_context_message.svg"; -$copy-button-url: "$(res)/img/icon_copy_message.svg"; - -// e2e -$e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color -$e2e-unverified-color: #e8bf37; -$e2e-warning-color: #ba6363; - -/*** ImageView ***/ -$lightbox-bg-color: #454545; -$lightbox-fg-color: #ffffff; -$lightbox-border-color: #ffffff; - -$imagebody-giflabel: rgba(0, 0, 0, 0.7); -$imagebody-giflabel-border: rgba(0, 0, 0, 0.2); -$imagebody-giflabel-color: rgba(255, 255, 255, 1); - -// Tabbed views -$tab-label-fg-color: #45474a; -$tab-label-active-fg-color: #ffffff; -$tab-label-bg-color: transparent; -$tab-label-active-bg-color: #7ac9a1; -$tab-label-icon-bg-color: #454545; -$tab-label-active-icon-bg-color: #ffffff; - -// Buttons -$button-primary-fg-color: #ffffff; -$button-primary-bg-color: #7ac9a1; -$button-primary-disabled-fg-color: #ffffff; -$button-primary-disabled-bg-color: #bce4d0; -$button-danger-fg-color: #ffffff; -$button-danger-bg-color: #f56679; -$button-danger-disabled-fg-color: #ffffff; -$button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color - -// Toggle switch -$togglesw-off-color: #c1c9d6; -$togglesw-on-color: #7ac9a1; -$togglesw-ball-color: #fff; - -// unused? -$progressbar-color: #000; - -$room-warning-bg-color: #fff8e3; - -$memberstatus-placeholder-color: $roomtile-name-color; - -$authpage-bg-color: #2e3649; -$authpage-modal-bg-color: rgba(255, 255, 255, 0.59); -$authpage-body-bg-color: #ffffff; -$authpage-lang-color: #4e5054; -$authpage-body-color: #61708b; - -// ***** Mixins! ***** - -@define-mixin mx_DialogButton { - /* align images in buttons (eg spinners) */ - vertical-align: middle; - border: 0px; - border-radius: 36px; - font-family: $font-family; - font-size: 14px; - color: $accent-fg-color; - background-color: $accent-color; - width: auto; - padding: 7px; - padding-left: 1.5em; - padding-right: 1.5em; - cursor: pointer; - display: inline-block; - outline: none; -} - -@define-mixin mx_DialogButton_danger { - background-color: $warning-color; -} - -@define-mixin mx_DialogButton_hover { -} - -@define-mixin mx_DialogButton_small { - @mixin mx_DialogButton; - font-size: 15px; - padding: 0px 1.5em 0px 1.5em; -} - -@define-mixin mx_DialogButton_secondary { - // flip colours for the secondary ones - font-weight: 600; - border: 1px solid $accent-color ! important; - color: $accent-color; - background-color: $accent-fg-color; -} diff --git a/res/themes/light/css/_fonts.scss b/res/themes/light/css/_fonts.scss index c080663acf..1bc9b5a4a3 100644 --- a/res/themes/light/css/_fonts.scss +++ b/res/themes/light/css/_fonts.scss @@ -1,67 +1,84 @@ /* - * Open Sans - * Includes extended Latin, Greek, Cyrillic and Vietnamese character sets + * Nunito. + * Includes extended Latin and Vietnamese character sets + * Current URLs are taken from + * https://github.com/alexeiva/NunitoFont/releases/tag/v3.500 + * ...in order to include cyrillic. + * + * Previously, they were + * https://fonts.googleapis.com/css?family=Nunito:400,400i,600,600i,700,700i&subset=latin-ext,vietnamese + * + * We explicitly do not include Nunito's italic variants, as they are not italic enough + * and it's better to rely on the browser's built-in obliquing behaviour. */ /* the 'src' links are relative to the bundle.css, which is in a subdirectory. */ @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Regular.ttf') format('truetype'); + font-family: 'Nunito'; + font-style: normal; font-weight: 400; + src: url('$(res)/fonts/Nunito/Nunito-Regular.ttf') format('truetype'); +} +@font-face { + font-family: 'Nunito'; font-style: normal; + font-weight: 600; + src: url('$(res)/fonts/Nunito/Nunito-SemiBold.ttf') format('truetype'); +} +@font-face { + font-family: 'Nunito'; + font-style: normal; + font-weight: 700; + src: url('$(res)/fonts/Nunito/Nunito-Bold.ttf') format('truetype'); } +/* latin-ext */ @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Italic.ttf') format('truetype'); + font-family: 'Inconsolata'; + font-style: normal; font-weight: 400; - font-style: italic; + src: local('Inconsolata Regular'), local('Inconsolata-Regular'), url('$(res)/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlX5qhExfHwNJU.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; } - +/* latin */ @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Semibold.ttf') format('truetype'); - font-weight: 600; + font-family: 'Inconsolata'; font-style: normal; + font-weight: 400; + font-display: swap; + src: local('Inconsolata Regular'), local('Inconsolata-Regular'), url('$(res)/fonts/Inconsolata/QldKNThLqRwH-OJ1UHjlKGlZ5qhExfHw.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } - +/* latin-ext */ @font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-SemiboldItalic.ttf') format('truetype'); - font-weight: 600; - font-style: italic; -} - -@font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-Bold.ttf') format('truetype'); - font-weight: 700; + font-family: 'Inconsolata'; font-style: normal; -} - -@font-face { - font-family: 'Open Sans'; - src: url('$(res)/fonts/Open_Sans/OpenSans-BoldItalic.ttf') format('truetype'); font-weight: 700; - font-style: italic; + font-display: swap; + src: local('Inconsolata Bold'), local('Inconsolata-Bold'), url('$(res)/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71n5_zaDpwm80E.woff2') format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Inconsolata'; + font-style: normal; + font-weight: 700; + font-display: swap; + src: local('Inconsolata Bold'), local('Inconsolata-Bold'), url('$(res)/fonts/Inconsolata/QldXNThLqRwH-OJ1UHjlKGHiw71p5_zaDpwm.woff2') format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } -/* - * Fira Mono - * Used for monospace copy, i.e. code +/* a COLR/CPAL version of Twemoji used for consistent cross-browser emoji + * taken from https://github.com/mozilla/twemoji-colr + * using the fix from https://github.com/mozilla/twemoji-colr/issues/50 to + * work on macOS */ - +/* +// except we now load it dynamically via FontManager to handle browsers +// which can't render COLR/CPAL still @font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; -} - -@font-face { - font-family: 'Fira Mono'; - src: url('$(res)/fonts/Fira_Mono/FiraMono-Bold.ttf') format('truetype'); - font-weight: 700; - font-style: normal; + font-family: "Twemoji Mozilla"; + src: url('$(res)/fonts/Twemoji_Mozilla/TwemojiMozilla.woff2') format('woff2'); } +*/ \ No newline at end of file diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/light/css/_light.scss similarity index 57% rename from res/themes/dharma/css/_dharma.scss rename to res/themes/light/css/_light.scss index e389cc944c..712d905b43 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/light/css/_light.scss @@ -1,13 +1,23 @@ // XXX: check this? /* Nunito lacks combining diacritics, so these will fall through to the next font. Helevetica's diacritics however do not combine - nicely with Open Sans (on OSX, at least) and result in a huge - horizontal mess. Arial empirically gets it right, hence prioritising - Arial here. */ -$font-family: 'Nunito', Arial, Helvetica, Sans-Serif; + nicely (on OSX, at least) and result in a huge horizontal mess. + Arial empirically gets it right, hence prioritising Arial here. */ +/* We fall through to Twemoji for emoji rather than falling through + to native Emoji fonts (if any) to ensure cross-browser consistency */ +$font-family: Nunito, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', Arial, Helvetica, Sans-Serif; + +$monospace-font-family: Inconsolata, Twemoji, 'Apple Color Emoji', 'Segoe UI Emoji', 'Noto Color Emoji', Courier, monospace; + +// unified palette +// try to use these colors when possible +$accent-color: #03b381; +$notice-primary-color: #ff4b55; +$notice-secondary-color: #61708b; +$header-panel-bg-color: #f3f8fd; // typical text (dark-on-white in light skin) -$primary-fg-color: #454545; +$primary-fg-color: #2e2f32; $primary-bg-color: #ffffff; // used for dialog box text @@ -18,7 +28,6 @@ $focus-bg-color: #dddddd; // button UI (white-on-green in light skin) $accent-fg-color: #ffffff; -$accent-color: #7ac9a1; $accent-color-50pct: #92caad; $accent-color-alt: #238CF5; @@ -27,7 +36,7 @@ $selection-fg-color: $primary-bg-color; $focus-brightness: 105%; // red warning colour -$warning-color: #f56679; +$warning-color: $notice-primary-color; // background colour for warnings $warning-bg-color: #DF2A8B; $info-bg-color: #2A9EDF; @@ -35,8 +44,8 @@ $mention-user-pill-bg-color: $warning-color; $other-user-pill-bg-color: rgba(0, 0, 0, 0.1); // pinned events indicator -$pinned-unread-color: #ff0064; // $warning-color -$pinned-color: #888; +$pinned-unread-color: $notice-primary-color; +$pinned-color: $notice-secondary-color; // informational plinth $info-plinth-bg-color: #f7f7f7; @@ -48,7 +57,7 @@ $preview-bar-bg-color: #f7f7f7; $secondary-accent-color: #f2f5f8; $tertiary-accent-color: #d3efe1; -$tagpanel-bg-color: #2e3649; +$tagpanel-bg-color: #27303a; // used by RoomDirectory permissions $plinth-bg-color: $secondary-accent-color; @@ -60,23 +69,24 @@ $droptarget-bg-color: rgba(255,255,255,0.5); $selected-color: $secondary-accent-color; // selected for hoverover & selected event tiles -$event-selected-color: #f7f7f7; +$event-selected-color: $header-panel-bg-color; // used for the hairline dividers in RoomView $primary-hairline-color: #e5e5e5; // used for the border of input text fields $input-border-color: #e7e7e7; -$input-darker-bg-color: rgba(193, 201, 214, 0.29); +$input-darker-bg-color: #e3e8f0; $input-darker-fg-color: #9fa9ba; $input-lighter-bg-color: #f2f5f8; $input-lighter-fg-color: $input-darker-fg-color; $input-focused-border-color: #238cf5; -$input-valid-border-color: #7ac9a1; +$input-valid-border-color: $accent-color; +$input-invalid-border-color: $warning-color; $field-focused-label-bg-color: #ffffff; -$button-bg-color: #7ac9a1; +$button-bg-color: $accent-color; $button-fg-color: white; // apart from login forms, which have stronger border @@ -89,8 +99,9 @@ $input-fg-color: rgba(74, 74, 74, 0.9); $scrollbar-thumb-color: rgba(0, 0, 0, 0.2); $scrollbar-track-color: transparent; // context menus -$menu-border-color: #ebedf8; +$menu-border-color: #e7e7e7; $menu-bg-color: #fff; +$menu-box-shadow-color: rgba(118, 131, 156, 0.6); $menu-selected-color: #f5f8fa; $avatar-initial-color: #ffffff; @@ -98,10 +109,10 @@ $avatar-bg-color: #ffffff; $h3-color: #3d3b39; -$dialog-title-fg-color: #454545; +$dialog-title-fg-color: #45474a; $dialog-backdrop-color: rgba(46, 48, 51, 0.38); $dialog-shadow-color: rgba(0, 0, 0, 0.48); -$dialog-close-fg-color: #9fa9ba; +$dialog-close-fg-color: #c1c1c1; $dialog-background-bg-color: #e9e9e9; $lightbox-background-bg-color: #000; @@ -125,7 +136,7 @@ $settings-profile-placeholder-bg-color: #e7e7e7; $settings-profile-overlay-bg-color: #000; $settings-profile-overlay-placeholder-bg-color: transparent; $settings-profile-overlay-fg-color: #fff; -$settings-profile-overlay-placeholder-fg-color: #454545; +$settings-profile-overlay-placeholder-fg-color: #2e2f32; $settings-subsection-fg-color: #61708b; $voip-decline-color: #f48080; @@ -138,11 +149,18 @@ $rte-group-pill-color: #aaa; $topleftmenu-color: #212121; $roomheader-color: #45474a; -$roomheader-addroom-color: #91A1C0; -$roomtopic-color: #9fa9ba; +$roomheader-addroom-bg-color: #91A1C0; +$roomheader-addroom-fg-color: $accent-fg-color; +$tagpanel-button-color: #91A1C0; +$roomheader-button-color: #91A1C0; +$groupheader-button-color: #91A1C0; +$rightpanel-button-color: #91A1C0; +$composer-button-color: #91A1C0; +$roomtopic-color: #9e9e9e; $eventtile-meta-color: $roomtopic-color; $composer-e2e-icon-color: #c9ced6; +$header-divider-color: #91A1C0; // ******************** @@ -152,14 +170,14 @@ $roomtile-notified-color: #212121; $roomtile-selected-bg-color: #fff; $roomtile-focused-bg-color: #fff; -$username-variant1-color: #1e7ddc; -$username-variant2-color: #a756a8; -$username-variant3-color: #7ac9a1; -$username-variant4-color: #f2809d; -$username-variant5-color: #ffc666; -$username-variant6-color: #76ddd7; -$username-variant7-color: #45529b; -$username-variant8-color: #bfd251; +$username-variant1-color: #368bd6; +$username-variant2-color: #ac3ba8; +$username-variant3-color: #03b381; +$username-variant4-color: #e64f7a; +$username-variant5-color: #ff812d; +$username-variant6-color: #2dc2c5; +$username-variant7-color: #5c56f5; +$username-variant8-color: #74d12c; $roomtile-transparent-focused-color: rgba(0, 0, 0, 0.1); @@ -188,7 +206,6 @@ $event-redacted-border-color: #cccccc; // event timestamp $event-timestamp-color: #acacac; -$edit-button-url: "$(res)/img/icon_context_message.svg"; $copy-button-url: "$(res)/img/icon_copy_message.svg"; // e2e @@ -205,26 +222,24 @@ $lightbox-border-color: #ffffff; $tab-label-fg-color: #45474a; $tab-label-active-fg-color: #ffffff; $tab-label-bg-color: transparent; -$tab-label-active-bg-color: #7ac9a1; +$tab-label-active-bg-color: $accent-color; $tab-label-icon-bg-color: #454545; $tab-label-active-icon-bg-color: #ffffff; // Buttons $button-primary-fg-color: #ffffff; -$button-primary-bg-color: #7ac9a1; -$button-primary-disabled-fg-color: #ffffff; -$button-primary-disabled-bg-color: #bce4d0; +$button-primary-bg-color: $accent-color; +$button-secondary-bg-color: $accent-fg-color; $button-danger-fg-color: #ffffff; -$button-danger-bg-color: #f56679; +$button-danger-bg-color: $notice-primary-color; $button-danger-disabled-fg-color: #ffffff; $button-danger-disabled-bg-color: #f5b6bb; // TODO: Verify color // Toggle switch $togglesw-off-color: #c1c9d6; -$togglesw-on-color: #7ac9a1; +$togglesw-on-color: $accent-color; $togglesw-ball-color: #fff; -// unused? $progressbar-color: #000; $room-warning-bg-color: #fff8e3; @@ -234,119 +249,27 @@ $memberstatus-placeholder-color: $roomtile-name-color; $authpage-bg-color: #2e3649; $authpage-modal-bg-color: rgba(255, 255, 255, 0.59); $authpage-body-bg-color: #ffffff; +$authpage-focus-bg-color: #dddddd; $authpage-lang-color: #4e5054; -$authpage-body-color: #61708b; +$authpage-primary-color: #232f32; +$authpage-secondary-color: #61708b; -/*** form elements ***/ +$dark-panel-bg-color: $secondary-accent-color; +$panel-gradient: rgba(242, 245, 248, 0), rgba(242, 245, 248, 1); -// .mx_textinput is a container for a text input -// + some other controls like buttons, ... -// it has the appearance of a text box so the controls -// appear to be part of the input +$message-action-bar-bg-color: $primary-bg-color; +$message-action-bar-fg-color: $primary-fg-color; +$message-action-bar-border-color: #e9edf1; +$message-action-bar-hover-border-color: $focus-bg-color; -.mx_Dialog, .mx_MatrixChat { +$reaction-row-button-bg-color: $header-panel-bg-color; +$reaction-row-button-border-color: #e9edf1; +$reaction-row-button-hover-border-color: $focus-bg-color; +$reaction-row-button-selected-bg-color: #e9fff9; +$reaction-row-button-selected-border-color: $accent-color; - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], - .mx_textinput { - display: block; - box-sizing: border-box; - background-color: transparent; - color: $input-darker-fg-color; - border-radius: 4px; - border: 1px solid #c1c1c1; - // these things should probably not be defined - // globally - margin: 9px; - flex: 0 0 auto; - } - - .mx_textinput { - display: flex; - align-items: center; - - > input[type=text], - > input[type=search] { - border: none; - flex: 1; - color: $primary-fg-color; - }, - input::placeholder { - color: $roomsublist-label-fg-color; - } - } -} - -input[type=text], -input[type=search], -input[type=password] { - padding: 9px; - font-family: $font-family; - font-size: 14px; - font-weight: 600; - min-width: 0; -} - -/*** panels ***/ -.dark-panel { - background-color: $secondary-accent-color; -} - -.dark-panel { - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], - .mx_textinput { - color: $input-darker-fg-color; - background-color: $input-darker-bg-color; - border: none; - } -} - -.light-panel { - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=text], - :not(.mx_textinput):not(.mx_Field):not(.mx_no_textinput) > input[type=search], - .mx_textinput { - color: $input-lighter-fg-color; - background-color: $input-lighter-bg-color; - border: none; - } -} - -input[type=text].mx_textinput_icon, -input[type=search].mx_textinput_icon { - padding-left: 36px; - background-repeat: no-repeat; - background-position: 10px center; -} - - -// FIXME THEME - Tint by CSS rather than referencing a duplicate asset -input[type=text].mx_textinput_icon.mx_textinput_search, -input[type=search].mx_textinput_icon.mx_textinput_search { - background-image: url('$(res)/img/feather-icons/search-input.svg'); -} - -// dont search UI as not all browsers support it, -// we implement it ourselves where needed instead -input[type=search]::-webkit-search-decoration, -input[type=search]::-webkit-search-cancel-button, -input[type=search]::-webkit-search-results-button, -input[type=search]::-webkit-search-results-decoration { - display: none; -} - -.input[type=text]::-webkit-input-placeholder, -.input[type=text]::-moz-placeholder, -.input[type=search]::-webkit-input-placeholder, -.input[type=search]::-moz-placeholder { - color: #a5aab2; -} - -// Override Firefox's UA style so we get a consistent look across browsers -input::placeholder, -textarea::placeholder { - opacity: initial; -} +$tooltip-timeline-bg-color: $tagpanel-bg-color; +$tooltip-timeline-fg-color: #ffffff; // ***** Mixins! ***** @@ -386,5 +309,5 @@ textarea::placeholder { font-weight: 600; border: 1px solid $accent-color ! important; color: $accent-color; - background-color: $accent-fg-color; + background-color: $button-secondary-bg-color; } diff --git a/res/themes/light/css/light.scss b/res/themes/light/css/light.scss index 6ac8d0feba..6acb2d9d94 100644 --- a/res/themes/light/css/light.scss +++ b/res/themes/light/css/light.scss @@ -1,4 +1,4 @@ @import "_paths.scss"; @import "_fonts.scss"; -@import "_base.scss"; +@import "_light.scss"; @import "../../../../res/css/_components.scss"; diff --git a/scripts/ci/Dockerfile b/scripts/ci/Dockerfile new file mode 100644 index 0000000000..c153d11cc7 --- /dev/null +++ b/scripts/ci/Dockerfile @@ -0,0 +1,9 @@ +# Update on docker hub with the following commands in the directory of this file: +# docker build -t matrixdotorg/riotweb-ci-e2etests-env:latest . +# docker log +# docker push matrixdotorg/riotweb-ci-e2etests-env:latest +FROM node:10 +RUN apt-get update +RUN apt-get -y install build-essential python3-dev libffi-dev python-pip python-setuptools sqlite3 libssl-dev python-virtualenv libjpeg-dev libxslt1-dev uuid-runtime +# dependencies for chrome (installed by puppeteer) +RUN apt-get -y install gconf-service libasound2 libatk1.0-0 libatk-bridge2.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget diff --git a/scripts/ci/build.sh b/scripts/ci/build.sh new file mode 100644 index 0000000000..0b1fa23093 --- /dev/null +++ b/scripts/ci/build.sh @@ -0,0 +1,25 @@ +#!/bin/bash +# +# script which is run by the CI build (after `yarn test`). +# +# clones riot-web develop and runs the tests against our version of react-sdk. + +set -ev + +RIOT_WEB_DIR=riot-web +REACT_SDK_DIR=`pwd` + +yarn link + +scripts/fetchdep.sh vector-im riot-web + +pushd "$RIOT_WEB_DIR" + +yarn link matrix-js-sdk +yarn link matrix-react-sdk + +yarn install + +yarn build + +popd diff --git a/scripts/ci/end-to-end-tests.sh b/scripts/ci/end-to-end-tests.sh new file mode 100644 index 0000000000..0ec26df450 --- /dev/null +++ b/scripts/ci/end-to-end-tests.sh @@ -0,0 +1,43 @@ +#!/bin/bash +# +# script which is run by the CI build (after `yarn test`). +# +# clones riot-web develop and runs the tests against our version of react-sdk. + +set -ev + +upload_logs() { + echo "--- Uploading logs" + buildkite-agent artifact upload "logs/**/*;synapse/installations/consent/homeserver.log" +} + +handle_error() { + EXIT_CODE=$? + if [ $TESTS_STARTED -eq 1 ]; then + upload_logs + fi + exit $EXIT_CODE +} + +trap 'handle_error' ERR + +RIOT_WEB_DIR=riot-web +REACT_SDK_DIR=`pwd` + + +echo "--- Building Riot" +scripts/ci/build.sh +# run end to end tests +echo "--- Fetching end-to-end tests from master" +scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master +pushd matrix-react-end-to-end-tests +ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web +# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh +# CHROME_PATH=$(which google-chrome-stable) ./run.sh +echo "--- Install synapse & other dependencies" +./install.sh +mkdir logs +echo "+++ Running end-to-end tests" +TESTS_STARTED=1 +./run.sh --no-sandbox --log-directory logs/ +popd diff --git a/scripts/ci/install-deps.sh b/scripts/ci/install-deps.sh new file mode 100644 index 0000000000..6484ebab29 --- /dev/null +++ b/scripts/ci/install-deps.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +set -ex + +scripts/fetchdep.sh matrix-org matrix-js-sdk + +pushd matrix-js-sdk +yarn link +yarn install +popd + +yarn link matrix-js-sdk +yarn install diff --git a/scripts/travis/riot-unit-tests.sh b/scripts/ci/riot-unit-tests.sh old mode 100755 new mode 100644 similarity index 60% rename from scripts/travis/riot-unit-tests.sh rename to scripts/ci/riot-unit-tests.sh index a2f0d61112..215af13030 --- a/scripts/travis/riot-unit-tests.sh +++ b/scripts/ci/riot-unit-tests.sh @@ -1,6 +1,6 @@ #!/bin/bash # -# script which is run by the travis build (after `npm run test`). +# script which is run by the CI build (after `yarn test`). # # clones riot-web develop and runs the tests against our version of react-sdk. @@ -8,7 +8,7 @@ set -ev RIOT_WEB_DIR=riot-web -scripts/travis/build.sh +scripts/ci/build.sh pushd "$RIOT_WEB_DIR" -npm run test +yarn test popd diff --git a/scripts/travis/unit-tests.sh b/scripts/ci/unit-tests.sh old mode 100755 new mode 100644 similarity index 50% rename from scripts/travis/unit-tests.sh rename to scripts/ci/unit-tests.sh index a8e0a63b31..5b86190963 --- a/scripts/travis/unit-tests.sh +++ b/scripts/ci/unit-tests.sh @@ -1,10 +1,10 @@ #!/bin/bash # -# script which is run by the travis build (after `npm run test`). +# script which is run by the CI build (after `yarn test`). # # clones riot-web develop and runs the tests against our version of react-sdk. set -ev -scripts/travis/build.sh -npm run test +scripts/ci/build.sh +yarn test diff --git a/scripts/emoji-data-strip.js b/scripts/emoji-data-strip.js index 42bf2ac2de..1c3738cab1 100644 --- a/scripts/emoji-data-strip.js +++ b/scripts/emoji-data-strip.js @@ -1,28 +1,29 @@ #!/usr/bin/env node -const EMOJI_DATA = require('emojione/emoji.json'); -const EMOJI_SUPPORTED = Object.keys(require('emojione').emojioneList); + +// This generates src/stripped-emoji.json as used by the EmojiProvider autocomplete +// provider. + +const EMOJIBASE = require('emojibase-data/en/compact.json'); + const fs = require('fs'); -const output = Object.keys(EMOJI_DATA).map( - (key) => { - const datum = EMOJI_DATA[key]; +const output = EMOJIBASE.map( + (datum) => { const newDatum = { - name: datum.name, - shortname: datum.shortname, - category: datum.category, - emoji_order: datum.emoji_order, + name: datum.annotation, + shortname: `:${datum.shortcodes[0]}:`, + category: datum.group, + emoji_order: datum.order, }; - if (datum.aliases.length > 0) { - newDatum.aliases = datum.aliases; + if (datum.shortcodes.length > 1) { + newDatum.aliases = datum.shortcodes.slice(1).map(s => `:${s}:`); } - if (datum.aliases_ascii.length > 0) { - newDatum.aliases_ascii = datum.aliases_ascii; + if (datum.emoticon) { + newDatum.aliases_ascii = [ datum.emoticon ]; } return newDatum; } -).filter((datum) => { - return EMOJI_SUPPORTED.includes(datum.shortname); -}); +); // Write to a file in src. Changes should be checked into git. This file is copied by // babel using --copy-files diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 95fc4b0603..f82752bfc5 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -1,27 +1,40 @@ -#!/bin/sh +#!/bin/bash -org="$1" -repo="$2" +set -x + +deforg="$1" +defrepo="$2" defbranch="$3" [ -z "$defbranch" ] && defbranch="develop" -rm -r "$repo" || true +rm -r "$defrepo" || true clone() { - branch=$1 + org=$1 + repo=$2 + branch=$3 if [ -n "$branch" ] then - echo "Trying to use the branch $branch" - git clone https://github.com/$org/$repo.git $repo --branch "$branch" && exit 0 + echo "Trying to use $org/$repo#$branch" + git clone git://github.com/$org/$repo.git $repo --branch "$branch" && exit 0 fi } # Try the PR author's branch in case it exists on the deps as well. -clone $TRAVIS_PULL_REQUEST_BRANCH +# If BUILDKITE_BRANCH is set, it will contain either: +# * "branch" when the author's branch and target branch are in the same repo +# * "author:branch" when the author's branch is in their fork +# We can split on `:` into an array to check. +BUILDKITE_BRANCH_ARRAY=(${BUILDKITE_BRANCH//:/ }) +if [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "1" ]]; then + clone $deforg $defrepo $BUILDKITE_BRANCH +elif [[ "${#BUILDKITE_BRANCH_ARRAY[@]}" == "2" ]]; then + clone ${BUILDKITE_BRANCH_ARRAY[0]} $defrepo ${BUILDKITE_BRANCH_ARRAY[1]} +fi # Try the target branch of the push or PR. -clone $TRAVIS_BRANCH +clone $deforg $defrepo $BUILDKITE_PULL_REQUEST_BASE_BRANCH # Try the current branch from Jenkins. -clone `"echo $GIT_BRANCH" | sed -e 's/^origin\///'` +clone $deforg $defrepo `"echo $GIT_BRANCH" | sed -e 's/^origin\///'` # Use the default branch as the last resort. -clone $defbranch +clone $deforg $defrepo $defbranch diff --git a/scripts/travis/build.sh b/scripts/travis/build.sh deleted file mode 100755 index a353e38a06..0000000000 --- a/scripts/travis/build.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# -# script which is run by the travis build (after `npm run test`). -# -# clones riot-web develop and runs the tests against our version of react-sdk. - -set -ev - -RIOT_WEB_DIR=riot-web -REACT_SDK_DIR=`pwd` - -scripts/fetchdep.sh vector-im riot-web -pushd "$RIOT_WEB_DIR" - -mkdir node_modules -npm install - -# use the version of js-sdk we just used in the react-sdk tests -rm -r node_modules/matrix-js-sdk -ln -s "$REACT_SDK_DIR/node_modules/matrix-js-sdk" node_modules/matrix-js-sdk - -# ... and, of course, the version of react-sdk we just built -rm -r node_modules/matrix-react-sdk -ln -s "$REACT_SDK_DIR" node_modules/matrix-react-sdk - -npm run build -popd diff --git a/scripts/travis/end-to-end-tests.sh b/scripts/travis/end-to-end-tests.sh deleted file mode 100755 index 361b053d2b..0000000000 --- a/scripts/travis/end-to-end-tests.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# -# script which is run by the travis build (after `npm run test`). -# -# clones riot-web develop and runs the tests against our version of react-sdk. - -set -ev - -RIOT_WEB_DIR=riot-web -REACT_SDK_DIR=`pwd` - -scripts/travis/build.sh -# run end to end tests -scripts/fetchdep.sh matrix-org matrix-react-end-to-end-tests master -pushd matrix-react-end-to-end-tests -ln -s $REACT_SDK_DIR/$RIOT_WEB_DIR riot/riot-web -# PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true ./install.sh -# CHROME_PATH=$(which google-chrome-stable) ./run.sh -./install.sh -./run.sh --travis -popd diff --git a/scripts/travis/install-deps.sh b/scripts/travis/install-deps.sh deleted file mode 100755 index 04cd728157..0000000000 --- a/scripts/travis/install-deps.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -set -ex -npm install -scripts/fetchdep.sh matrix-org matrix-js-sdk -rm -r node_modules/matrix-js-sdk || true -ln -s ../matrix-js-sdk node_modules/matrix-js-sdk - -cd matrix-js-sdk -npm install -cd .. diff --git a/src/Analytics.js b/src/Analytics.js index 3a8c5d6e10..3e208ad6bd 100644 --- a/src/Analytics.js +++ b/src/Analytics.js @@ -84,6 +84,11 @@ const customVariables = { expl: _td('Whether or not you\'re using the Richtext mode of the Rich Text Editor'), example: 'off', }, + 'Breadcrumbs': { + id: 9, + expl: _td("Whether or not you're using the 'breadcrumbs' feature (avatars above the room list)"), + example: 'disabled', + }, 'Homeserver URL': { id: 7, expl: _td('Your homeserver\'s URL'), @@ -201,6 +206,7 @@ class Analytics { trackEvent(category, action, name, value) { if (this.disabled) return; + this._paq.push(['setCustomUrl', getRedactedUrl()]); this._paq.push(['trackEvent', category, action, name, value]); } @@ -233,6 +239,11 @@ class Analytics { this._setVisitVariable('RTE: Uses Richtext Mode', state ? 'on' : 'off'); } + setBreadcrumbs(state) { + if (this.disabled) return; + this._setVisitVariable('Breadcrumbs', state ? 'enabled' : 'disabled'); + } + showDetailsModal() { let rows = []; if (window.Piwik) { @@ -267,7 +278,7 @@ class Analytics { const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('Analytics Details', '', ErrorDialog, { title: _t('Analytics'), - description:
+ description:
{ _t('The information being sent to us to help make Riot.im better includes:') }
diff --git a/src/Avatar.js b/src/Avatar.js index d3df12eb49..2e15874b4e 100644 --- a/src/Avatar.js +++ b/src/Avatar.js @@ -17,6 +17,7 @@ limitations under the License. 'use strict'; import {ContentRepo} from 'matrix-js-sdk'; import MatrixClientPeg from './MatrixClientPeg'; +import DMRoomMap from './utils/DMRoomMap'; module.exports = { avatarUrlForMember: function(member, width, height, resizeMethod) { @@ -51,11 +52,78 @@ module.exports = { }, defaultAvatarUrlForString: function(s) { - const images = ['76cfa6', '50e2c2', 'f4c371']; + const images = ['03b381', '368bd6', 'ac3ba8']; let total = 0; for (let i = 0; i < s.length; ++i) { total += s.charCodeAt(i); } return require('../res/img/' + images[total % images.length] + '.png'); }, + + /** + * returns the first (non-sigil) character of 'name', + * converted to uppercase + * @param {string} name + * @return {string} the first letter + */ + getInitialLetter(name) { + if (name.length < 1) { + return undefined; + } + + let idx = 0; + const initial = name[0]; + if ((initial === '@' || initial === '#' || initial === '+') && name[1]) { + idx++; + } + + // string.codePointAt(0) would do this, but that isn't supported by + // some browsers (notably PhantomJS). + let chars = 1; + const first = name.charCodeAt(idx); + + // check if it’s the start of a surrogate pair + if (first >= 0xD800 && first <= 0xDBFF && name[idx+1]) { + const second = name.charCodeAt(idx+1); + if (second >= 0xDC00 && second <= 0xDFFF) { + chars++; + } + } + + const firstChar = name.substring(idx, idx+chars); + return firstChar.toUpperCase(); + }, + + avatarUrlForRoom(room, width, height, resizeMethod) { + const explicitRoomAvatar = room.getAvatarUrl( + MatrixClientPeg.get().getHomeserverUrl(), + width, + height, + resizeMethod, + false, + ); + if (explicitRoomAvatar) { + return explicitRoomAvatar; + } + + let otherMember = null; + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(room.roomId); + if (otherUserId) { + otherMember = room.getMember(otherUserId); + } else { + // if the room is not marked as a 1:1, but only has max 2 members + // then still try to show any avatar (pref. other member) + otherMember = room.getAvatarFallbackMember(); + } + if (otherMember) { + return otherMember.getAvatarUrl( + MatrixClientPeg.get().getHomeserverUrl(), + width, + height, + resizeMethod, + false, + ); + } + return null; + }, }; diff --git a/src/BasePlatform.js b/src/BasePlatform.js index 79f0d69e2c..54310d1849 100644 --- a/src/BasePlatform.js +++ b/src/BasePlatform.js @@ -113,4 +113,29 @@ export default class BasePlatform { reload() { throw new Error("reload not implemented!"); } + + supportsAutoLaunch(): boolean { + return false; + } + + // XXX: Surely this should be a setting like any other? + async getAutoLaunchEnabled(): boolean { + return false; + } + + async setAutoLaunchEnabled(enabled: boolean): void { + throw new Error("Unimplemented"); + } + + supportsMinimizeToTray(): boolean { + return false; + } + + async getMinimizeToTrayEnabled(): boolean { + return false; + } + + async setMinimizeToTrayEnabled(enabled: boolean): void { + throw new Error("Unimplemented"); + } } diff --git a/src/CallHandler.js b/src/CallHandler.js index acdc3e5122..e47209eebe 100644 --- a/src/CallHandler.js +++ b/src/CallHandler.js @@ -361,7 +361,7 @@ async function _startCallApp(roomId, type) { Modal.createTrackedDialog('Could not connect to the integration server', '', ErrorDialog, { title: _t('Could not connect to the integration server'), - description: _t('A conference call could not be started because the intgrations server is not available'), + description: _t('A conference call could not be started because the integrations server is not available'), }); return; } diff --git a/src/ComposerHistoryManager.js b/src/ComposerHistoryManager.js deleted file mode 100644 index ecf773f2e7..0000000000 --- a/src/ComposerHistoryManager.js +++ /dev/null @@ -1,86 +0,0 @@ -//@flow -/* -Copyright 2017 Aviral Dasgupta - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { Value } from 'slate'; - -import _clamp from 'lodash/clamp'; - -type MessageFormat = 'rich' | 'markdown'; - -class HistoryItem { - // We store history items in their native format to ensure history is accurate - // and then convert them if our RTE has subsequently changed format. - value: Value; - format: MessageFormat = 'rich'; - - constructor(value: ?Value, format: ?MessageFormat) { - this.value = value; - this.format = format; - } - - static fromJSON(obj: Object): HistoryItem { - return new HistoryItem( - Value.fromJSON(obj.value), - obj.format, - ); - } - - toJSON(): Object { - return { - value: this.value.toJSON(), - format: this.format, - }; - } -} - -export default class ComposerHistoryManager { - history: Array = []; - prefix: string; - lastIndex: number = 0; // used for indexing the storage - currentIndex: number = 0; // used for indexing the loaded validated history Array - - constructor(roomId: string, prefix: string = 'mx_composer_history_') { - this.prefix = prefix + roomId; - - // TODO: Performance issues? - let item; - for (; item = sessionStorage.getItem(`${this.prefix}[${this.currentIndex}]`); this.currentIndex++) { - try { - this.history.push( - HistoryItem.fromJSON(JSON.parse(item)), - ); - } catch (e) { - console.warn("Throwing away unserialisable history", e); - } - } - this.lastIndex = this.currentIndex; - // reset currentIndex to account for any unserialisable history - this.currentIndex = this.history.length; - } - - save(value: Value, format: MessageFormat) { - const item = new HistoryItem(value, format); - this.history.push(item); - this.currentIndex = this.history.length; - sessionStorage.setItem(`${this.prefix}[${this.lastIndex++}]`, JSON.stringify(item.toJSON())); - } - - getItem(offset: number): ?HistoryItem { - this.currentIndex = _clamp(this.currentIndex + offset, 0, this.history.length - 1); - return this.history[this.currentIndex]; - } -} diff --git a/src/ContentMessages.js b/src/ContentMessages.js index a319118121..ee3e8f1390 100644 --- a/src/ContentMessages.js +++ b/src/ContentMessages.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,21 +18,27 @@ limitations under the License. 'use strict'; import Promise from 'bluebird'; -const extend = require('./extend'); -const dis = require('./dispatcher'); -const MatrixClientPeg = require('./MatrixClientPeg'); -const sdk = require('./index'); +import extend from './extend'; +import dis from './dispatcher'; +import MatrixClientPeg from './MatrixClientPeg'; +import sdk from './index'; import { _t } from './languageHandler'; -const Modal = require('./Modal'); - -const encrypt = require("browser-encrypt-attachment"); +import Modal from './Modal'; +import RoomViewStore from './stores/RoomViewStore'; +import encrypt from "browser-encrypt-attachment"; +import extractPngChunks from "png-chunks-extract"; // Polyfill for Canvas.toBlob API using Canvas.toDataURL -require("blueimp-canvas-to-blob"); +import "blueimp-canvas-to-blob"; const MAX_WIDTH = 800; const MAX_HEIGHT = 600; +// scraped out of a macOS hidpi (5660ppm) screenshot png +// 5669 px (x-axis) , 5669 px (y-axis) , per metre +const PHYS_HIDPI = [0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, 0x25, 0x01]; + +export class UploadCanceledError extends Error {} /** * Create a thumbnail for a image DOM element. @@ -91,27 +98,51 @@ function createThumbnail(element, inputWidth, inputHeight, mimeType) { /** * Load a file into a newly created image element. * - * @param {File} file The file to load in an image element. + * @param {File} imageFile The file to load in an image element. * @return {Promise} A promise that resolves with the html image element. */ -function loadImageElement(imageFile) { - const deferred = Promise.defer(); - +async function loadImageElement(imageFile) { // Load the file into an html element const img = document.createElement("img"); const objectUrl = URL.createObjectURL(imageFile); + const imgPromise = new Promise((resolve, reject) => { + img.onload = function() { + URL.revokeObjectURL(objectUrl); + resolve(img); + }; + img.onerror = function(e) { + reject(e); + }; + }); img.src = objectUrl; - // Once ready, create a thumbnail - img.onload = function() { - URL.revokeObjectURL(objectUrl); - deferred.resolve(img); - }; - img.onerror = function(e) { - deferred.reject(e); - }; + // check for hi-dpi PNGs and fudge display resolution as needed. + // this is mainly needed for macOS screencaps + let parsePromise; + if (imageFile.type === "image/png") { + // in practice macOS happens to order the chunks so they fall in + // the first 0x1000 bytes (thanks to a massive ICC header). + // Thus we could slice the file down to only sniff the first 0x1000 + // bytes (but this makes extractPngChunks choke on the corrupt file) + const headers = imageFile; //.slice(0, 0x1000); + parsePromise = readFileAsArrayBuffer(headers).then(arrayBuffer => { + const buffer = new Uint8Array(arrayBuffer); + const chunks = extractPngChunks(buffer); + for (const chunk of chunks) { + if (chunk.name === 'pHYs') { + if (chunk.data.byteLength !== PHYS_HIDPI.length) return; + const hidpi = chunk.data.every((val, i) => val === PHYS_HIDPI[i]); + return hidpi; + } + } + return false; + }); + } - return deferred.promise; + const [hidpi] = await Promise.all([parsePromise, imgPromise]); + const width = hidpi ? (img.width >> 1) : img.width; + const height = hidpi ? (img.height >> 1) : img.height; + return {width, height, img}; } /** @@ -119,7 +150,7 @@ function loadImageElement(imageFile) { * * @param {MatrixClient} matrixClient A matrixClient to upload the thumbnail with. * @param {String} roomId The ID of the room the image will be uploaded in. - * @param {File} The image to read and thumbnail. + * @param {File} imageFile The image to read and thumbnail. * @return {Promise} A promise that resolves with the attachment info. */ function infoForImageFile(matrixClient, roomId, imageFile) { @@ -129,8 +160,8 @@ function infoForImageFile(matrixClient, roomId, imageFile) { } let imageInfo; - return loadImageElement(imageFile).then(function(img) { - return createThumbnail(img, img.width, img.height, thumbnailType); + return loadImageElement(imageFile).then(function(r) { + return createThumbnail(r.img, r.width, r.height, thumbnailType); }).then(function(result) { imageInfo = result.info; return uploadFile(matrixClient, roomId, result.thumbnail); @@ -144,7 +175,7 @@ function infoForImageFile(matrixClient, roomId, imageFile) { /** * Load a file into a newly created video element. * - * @param {File} file The file to load in an video element. + * @param {File} videoFile The file to load in an video element. * @return {Promise} A promise that resolves with the video image element. */ function loadVideoElement(videoFile) { @@ -179,7 +210,7 @@ function loadVideoElement(videoFile) { * * @param {MatrixClient} matrixClient A matrixClient to upload the thumbnail with. * @param {String} roomId The ID of the room the video will be uploaded to. - * @param {File} The video to read and thumbnail. + * @param {File} videoFile The video to read and thumbnail. * @return {Promise} A promise that resolves with the attachment info. */ function infoForVideoFile(matrixClient, roomId, videoFile) { @@ -200,6 +231,7 @@ function infoForVideoFile(matrixClient, roomId, videoFile) { /** * Read the file as an ArrayBuffer. + * @param {File} file The file to read * @return {Promise} A promise that resolves with an ArrayBuffer when the file * is read. */ @@ -233,28 +265,40 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { if (matrixClient.isRoomEncrypted(roomId)) { // If the room is encrypted then encrypt the file before uploading it. // First read the file into memory. - return readFileAsArrayBuffer(file).then(function(data) { + let canceled = false; + let uploadPromise; + let encryptInfo; + const prom = readFileAsArrayBuffer(file).then(function(data) { + if (canceled) throw new UploadCanceledError(); // Then encrypt the file. return encrypt.encryptAttachment(data); }).then(function(encryptResult) { + if (canceled) throw new UploadCanceledError(); // Record the information needed to decrypt the attachment. - const encryptInfo = encryptResult.info; + encryptInfo = encryptResult.info; // Pass the encrypted data as a Blob to the uploader. const blob = new Blob([encryptResult.data]); - return matrixClient.uploadContent(blob, { + uploadPromise = matrixClient.uploadContent(blob, { progressHandler: progressHandler, includeFilename: false, - }).then(function(url) { - // If the attachment is encrypted then bundle the URL along - // with the information needed to decrypt the attachment and - // add it under a file key. - encryptInfo.url = url; - if (file.type) { - encryptInfo.mimetype = file.type; - } - return {"file": encryptInfo}; }); + + return uploadPromise; + }).then(function(url) { + // If the attachment is encrypted then bundle the URL along + // with the information needed to decrypt the attachment and + // add it under a file key. + encryptInfo.url = url; + if (file.type) { + encryptInfo.mimetype = file.type; + } + return {"file": encryptInfo}; }); + prom.abort = () => { + canceled = true; + if (uploadPromise) MatrixClientPeg.get().cancelUpload(uploadPromise); + }; + return prom; } else { const basePromise = matrixClient.uploadContent(file, { progressHandler: progressHandler, @@ -269,11 +313,43 @@ function uploadFile(matrixClient, roomId, file, progressHandler) { } } - -class ContentMessages { +export default class ContentMessages { constructor() { this.inprogress = []; this.nextId = 0; + this._mediaConfig = null; + } + + static sharedInstance() { + if (global.mx_ContentMessages === undefined) { + global.mx_ContentMessages = new ContentMessages(); + } + return global.mx_ContentMessages; + } + + _isFileSizeAcceptable(file) { + if (this._mediaConfig !== null && + this._mediaConfig["m.upload.size"] !== undefined && + file.size > this._mediaConfig["m.upload.size"]) { + return false; + } + return true; + } + + _ensureMediaConfigFetched() { + if (this._mediaConfig !== null) return; + + console.log("[Media Config] Fetching"); + return MatrixClientPeg.get().getMediaConfig().then((config) => { + console.log("[Media Config] Fetched config:", config); + return config; + }).catch(() => { + // Media repo can't or won't report limits, so provide an empty object (no limits). + console.log("[Media Config] Could not fetch config, so not limiting uploads."); + return {}; + }).then((config) => { + this._mediaConfig = config; + }); } sendStickerContentToRoom(url, roomId, info, text, matrixClient) { @@ -283,7 +359,90 @@ class ContentMessages { }); } - sendContentToRoom(file, roomId, matrixClient) { + getUploadLimit() { + if (this._mediaConfig !== null && this._mediaConfig["m.upload.size"] !== undefined) { + return this._mediaConfig["m.upload.size"]; + } else { + return null; + } + } + + async sendContentListToRoom(files, roomId, matrixClient) { + if (matrixClient.isGuest()) { + dis.dispatch({action: 'require_registration'}); + return; + } + + const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); + if (isQuoting) { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + const shouldUpload = await new Promise((resolve) => { + Modal.createTrackedDialog('Upload Reply Warning', '', QuestionDialog, { + title: _t('Replying With Files'), + description: ( +
{_t( + 'At this time it is not possible to reply with a file. ' + + 'Would you like to upload this file without replying?', + )}
+ ), + hasCancelButton: true, + button: _t("Continue"), + onFinished: (shouldUpload) => { + resolve(shouldUpload); + }, + }); + }); + if (!shouldUpload) return; + } + + await this._ensureMediaConfigFetched(); + + const tooBigFiles = []; + const okFiles = []; + + for (let i = 0; i < files.length; ++i) { + if (this._isFileSizeAcceptable(files[i])) { + okFiles.push(files[i]); + } else { + tooBigFiles.push(files[i]); + } + } + + if (tooBigFiles.length > 0) { + const UploadFailureDialog = sdk.getComponent("dialogs.UploadFailureDialog"); + const uploadFailureDialogPromise = new Promise((resolve) => { + Modal.createTrackedDialog('Upload Failure', '', UploadFailureDialog, { + badFiles: tooBigFiles, + totalFiles: files.length, + contentMessages: this, + onFinished: (shouldContinue) => { + resolve(shouldContinue); + }, + }); + }); + const shouldContinue = await uploadFailureDialogPromise; + if (!shouldContinue) return; + } + + const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); + for (let i = 0; i < okFiles.length; ++i) { + const file = okFiles[i]; + const shouldContinue = await new Promise((resolve) => { + Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + file, + currentIndex: i, + totalFiles: okFiles.length, + onFinished: (shouldContinue) => { + resolve(shouldContinue); + }, + }); + }); + if (!shouldContinue) break; + this._sendContentToRoom(file, roomId, matrixClient); + } + } + + _sendContentToRoom(file, roomId, matrixClient) { const content = { body: file.name || 'Attachment', info: { @@ -333,6 +492,9 @@ class ContentMessages { this.inprogress.push(upload); dis.dispatch({action: 'upload_started'}); + // Focus the composer view + dis.dispatch({action: 'focus_composer'}); + let error; function onProgress(ev) { @@ -357,9 +519,12 @@ class ContentMessages { }, function(err) { error = err; if (!upload.canceled) { - let desc = _t('The file \'%(fileName)s\' failed to upload', {fileName: upload.fileName}) + '.'; + let desc = _t("The file '%(fileName)s' failed to upload.", {fileName: upload.fileName}); if (err.http_status == 413) { - desc = _t('The file \'%(fileName)s\' exceeds this homeserver\'s size limit for uploads', {fileName: upload.fileName}); + desc = _t( + "The file '%(fileName)s' exceeds this homeserver's size limit for uploads", + {fileName: upload.fileName}, + ); } const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); Modal.createTrackedDialog('Upload failed', '', ErrorDialog, { @@ -377,15 +542,22 @@ class ContentMessages { } } if (error) { + // 413: File was too big or upset the server in some way: + // clear the media size limit so we fetch it again next time + // we try to upload + if (error && error.http_status === 413) { + this._mediaConfig = null; + } dis.dispatch({action: 'upload_failed', upload, error}); } else { dis.dispatch({action: 'upload_finished', upload}); + dis.dispatch({action: 'message_sent'}); } }); } getCurrentUploads() { - return this.inprogress; + return this.inprogress.filter(u => !u.canceled); } cancelUpload(promise) { @@ -401,12 +573,7 @@ class ContentMessages { if (upload) { upload.canceled = true; MatrixClientPeg.get().cancelUpload(upload.promise); + dis.dispatch({action: 'upload_canceled', upload}); } } } - -if (global.mx_ContentMessage === undefined) { - global.mx_ContentMessage = new ContentMessages(); -} - -module.exports = global.mx_ContentMessage; diff --git a/src/FromWidgetPostMessageApi.js b/src/FromWidgetPostMessageApi.js index ea7eeba756..61c51d4a20 100644 --- a/src/FromWidgetPostMessageApi.js +++ b/src/FromWidgetPostMessageApi.js @@ -1,5 +1,6 @@ /* Copyright 2018 New Vector Ltd +Copyright 2019 Travis Ralston Licensed under the Apache License, Version 2.0 (the 'License'); you may not use this file except in compliance with the License. @@ -20,17 +21,19 @@ import IntegrationManager from './IntegrationManager'; import WidgetMessagingEndpoint from './WidgetMessagingEndpoint'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; -const WIDGET_API_VERSION = '0.0.1'; // Current API version +const WIDGET_API_VERSION = '0.0.2'; // Current API version const SUPPORTED_WIDGET_API_VERSIONS = [ '0.0.1', + '0.0.2', ]; const INBOUND_API_NAME = 'fromWidget'; -// Listen for and handle incomming requests using the 'fromWidget' postMessage +// Listen for and handle incoming requests using the 'fromWidget' postMessage // API and initiate responses export default class FromWidgetPostMessageApi { constructor() { this.widgetMessagingEndpoints = []; + this.widgetListeners = {}; // {action: func[]} this.start = this.start.bind(this); this.stop = this.stop.bind(this); @@ -45,6 +48,32 @@ export default class FromWidgetPostMessageApi { window.removeEventListener('message', this.onPostMessage); } + /** + * Adds a listener for a given action + * @param {string} action The action to listen for. + * @param {Function} callbackFn A callback function to be called when the action is + * encountered. Called with two parameters: the interesting request information and + * the raw event received from the postMessage API. The raw event is meant to be used + * for sendResponse and similar functions. + */ + addListener(action, callbackFn) { + if (!this.widgetListeners[action]) this.widgetListeners[action] = []; + this.widgetListeners[action].push(callbackFn); + } + + /** + * Removes a listener for a given action. + * @param {string} action The action that was subscribed to. + * @param {Function} callbackFn The original callback function that was used to subscribe + * to updates. + */ + removeListener(action, callbackFn) { + if (!this.widgetListeners[action]) return; + + const idx = this.widgetListeners[action].indexOf(callbackFn); + if (idx !== -1) this.widgetListeners[action].splice(idx, 1); + } + /** * Register a widget endpoint for trusted postMessage communication * @param {string} widgetId Unique widget identifier @@ -87,10 +116,8 @@ export default class FromWidgetPostMessageApi { const origin = u.protocol + '//' + u.host; if (this.widgetMessagingEndpoints && this.widgetMessagingEndpoints.length > 0) { const length = this.widgetMessagingEndpoints.length; - this.widgetMessagingEndpoints = this.widgetMessagingEndpoints. - filter(function(endpoint) { - return (endpoint.widgetId != widgetId || endpoint.endpointUrl != origin); - }); + this.widgetMessagingEndpoints = this.widgetMessagingEndpoints + .filter((endpoint) => endpoint.widgetId !== widgetId || endpoint.endpointUrl !== origin); return (length > this.widgetMessagingEndpoints.length); } return false; @@ -117,6 +144,13 @@ export default class FromWidgetPostMessageApi { return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise } + // Call any listeners we have registered + if (this.widgetListeners[event.data.action]) { + for (const fn of this.widgetListeners[event.data.action]) { + fn(event.data, event); + } + } + // Although the requestId is required, we don't use it. We'll be nice and process the message // if the property is missing, but with a warning for widget developers. if (!event.data.requestId) { @@ -164,6 +198,8 @@ export default class FromWidgetPostMessageApi { if (ActiveWidgetStore.widgetHasCapability(widgetId, 'm.always_on_screen')) { ActiveWidgetStore.setWidgetPersistence(widgetId, val); } + } else if (action === 'get_openid') { + // Handled by caller } else { console.warn('Widget postMessage event unhandled'); this.sendError(event, {message: 'The postMessage was unhandled'}); diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index 532ee23c25..cd5ecc790d 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -38,7 +38,7 @@ export function showGroupInviteDialog(groupId) { Modal.createTrackedDialog('Group Invite', '', AddressPickerDialog, { title: _t("Invite new community members"), description: description, - placeholder: _t("Name or matrix ID"), + placeholder: _t("Name or Matrix ID"), button: _t("Invite to Community"), validAddressTypes: ['mx-user-id'], onFinished: (success, addrs) => { diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index e72c0bfe4b..a8bbce10dc 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -19,39 +19,46 @@ limitations under the License. import ReplyThread from "./components/views/elements/ReplyThread"; -const React = require('react'); -const sanitizeHtml = require('sanitize-html'); -const highlight = require('highlight.js'); -const linkifyMatrix = require('./linkify-matrix'); +import React from 'react'; +import sanitizeHtml from 'sanitize-html'; +import highlight from 'highlight.js'; +import * as linkify from 'linkifyjs'; +import linkifyMatrix from './linkify-matrix'; +import _linkifyElement from 'linkifyjs/element'; +import _linkifyString from 'linkifyjs/string'; import escape from 'lodash/escape'; -import emojione from 'emojione'; import classNames from 'classnames'; import MatrixClientPeg from './MatrixClientPeg'; import url from 'url'; -emojione.imagePathSVG = 'emojione/svg/'; -// Store PNG path for displaying many flags at once (for increased performance over SVG) -emojione.imagePathPNG = 'emojione/png/'; -// Use SVGs for emojis -emojione.imageType = 'svg'; +import EMOJIBASE from 'emojibase-data/en/compact.json'; +import EMOJIBASE_REGEX from 'emojibase-regex'; + +linkifyMatrix(linkify); // Anything outside the basic multilingual plane will be a surrogate pair const SURROGATE_PAIR_PATTERN = /([\ud800-\udbff])([\udc00-\udfff])/; -// And there a bunch more symbol characters that emojione has within the +// And there a bunch more symbol characters that emojibase has within the // BMP, so this includes the ranges from 'letterlike symbols' to // 'miscellaneous symbols and arrows' which should catch all of them // (with plenty of false positives, but that's OK) const SYMBOL_PATTERN = /([\u2100-\u2bff])/; -// And this is emojione's complete regex -const EMOJI_REGEX = new RegExp(emojione.unicodeRegexp+"+", "gi"); +// Regex pattern for Zero-Width joiner unicode characters +const ZWJ_REGEX = new RegExp("\u200D|\u2003", "g"); + +// Regex pattern for whitespace characters +const WHITESPACE_REGEX = new RegExp("\\s", "g"); + +const BIGEMOJI_REGEX = new RegExp(`^(${EMOJIBASE_REGEX.source})+$`, 'i'); + const COLOR_REGEX = /^#[0-9a-fA-F]{6}$/; const PERMITTED_URL_SCHEMES = ['http', 'https', 'ftp', 'mailto', 'magnet']; /* * Return true if the given string contains emoji - * Uses a much, much simpler regex than emojione's so will give false + * Uses a much, much simpler regex than emojibase's so will give false * positives, but useful for fast-path testing strings to see if they * need emojification. * unicodeToImage uses this function. @@ -60,56 +67,27 @@ export function containsEmoji(str) { return SURROGATE_PAIR_PATTERN.test(str) || SYMBOL_PATTERN.test(str); } -/* modified from https://github.com/Ranks/emojione/blob/master/lib/js/emojione.js - * because we want to include emoji shortnames in title text +/** + * Returns the shortcode for an emoji character. + * + * @param {String} char The emoji character + * @return {String} The shortcode (such as :thumbup:) */ -function unicodeToImage(str) { - let replaceWith; let unicode; let alt; let short; let fname; - const mappedUnicode = emojione.mapUnicodeToShort(); - - str = str.replace(emojione.regUnicode, function(unicodeChar) { - if ( (typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap)) ) { - // if the unicodeChar doesnt exist just return the entire match - return unicodeChar; - } else { - // get the unicode codepoint from the actual char - unicode = emojione.jsEscapeMap[unicodeChar]; - - short = mappedUnicode[unicode]; - fname = emojione.emojioneList[short].fname; - - // depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname - alt = (emojione.unicodeAlt) ? emojione.convert(unicode.toUpperCase()) : mappedUnicode[unicode]; - const title = mappedUnicode[unicode]; - - replaceWith = `${alt}`; - return replaceWith; - } - }); - - return str; +export function unicodeToShortcode(char) { + const data = EMOJIBASE.find(e => e.unicode === char); + return (data && data.shortcodes ? `:${data.shortcodes[0]}:` : ''); } /** - * Given one or more unicode characters (represented by unicode - * character number), return an image node with the corresponding - * emoji. + * Returns the unicode character for an emoji shortcode * - * @param alt {string} String to use for the image alt text - * @param useSvg {boolean} Whether to use SVG image src. If False, PNG will be used. - * @param unicode {integer} One or more integers representing unicode characters - * @returns A img node with the corresponding emoji + * @param {String} shortcode The shortcode (such as :thumbup:) + * @return {String} The emoji character; null if none exists */ -export function charactersToImageNode(alt, useSvg, ...unicode) { - const fileName = unicode.map((u) => { - return u.toString(16); - }).join('-'); - const path = useSvg ? emojione.imagePathSVG : emojione.imagePathPNG; - const fileType = useSvg ? 'svg' : 'png'; - return {alt}; +export function shortcodeToUnicode(shortcode) { + shortcode = shortcode.slice(1, shortcode.length - 1); + const data = EMOJIBASE.find(e => e.shortcodes && e.shortcodes.includes(shortcode)); + return data ? data.unicode : null; } export function processHtmlForSending(html: string): string { @@ -220,7 +198,7 @@ const transformTags = { // custom to matrix 'code': function(tagName, attribs) { if (typeof attribs.class !== 'undefined') { // Filter out all classes other than ones starting with language- for syntax highlighting. - const classes = attribs.class.split(/\s+/).filter(function(cl) { + const classes = attribs.class.split(/\s/).filter(function(cl) { return cl.startsWith('language-'); }); attribs.class = classes.join(' '); @@ -416,13 +394,10 @@ class TextHighlighter extends BaseHighlighter { * opts.disableBigEmoji: optional argument to disable the big emoji class. * opts.stripReplyFallback: optional argument specifying the event is a reply and so fallback needs removing * opts.returnString: return an HTML string rather than JSX elements - * opts.emojiOne: optional param to do emojiOne (default true) * opts.forComposerQuote: optional param to lessen the url rewriting done by sanitization, for quoting into composer */ export function bodyToHtml(content, highlights, opts={}) { const isHtmlMessage = content.format === "org.matrix.custom.html" && content.formatted_body; - - const doEmojiOne = opts.emojiOne === undefined ? true : opts.emojiOne; let bodyHasEmoji = false; let sanitizeParams = sanitizeHtmlParams; @@ -453,28 +428,12 @@ export function bodyToHtml(content, highlights, opts={}) { if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody); strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(content.body) : content.body; - if (doEmojiOne) { - bodyHasEmoji = containsEmoji(isHtmlMessage ? formattedBody : content.body); - } + bodyHasEmoji = containsEmoji(isHtmlMessage ? formattedBody : content.body); // Only generate safeBody if the message was sent as org.matrix.custom.html if (isHtmlMessage) { isDisplayedWithHtml = true; safeBody = sanitizeHtml(formattedBody, sanitizeParams); - } else { - // ... or if there are emoji, which we insert as HTML alongside the - // escaped plaintext body. - if (bodyHasEmoji) { - isDisplayedWithHtml = true; - safeBody = sanitizeHtml(escape(strippedBody), sanitizeParams); - } - } - - // An HTML message with emoji - // or a plaintext message with emoji that was escaped and sanitized into - // HTML. - if (bodyHasEmoji) { - safeBody = unicodeToImage(safeBody); } } finally { delete sanitizeParams.textFilter; @@ -486,25 +445,65 @@ export function bodyToHtml(content, highlights, opts={}) { let emojiBody = false; if (!opts.disableBigEmoji && bodyHasEmoji) { - EMOJI_REGEX.lastIndex = 0; - const contentBodyTrimmed = strippedBody !== undefined ? strippedBody.trim() : ''; - const match = EMOJI_REGEX.exec(contentBodyTrimmed); - emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length; + let contentBodyTrimmed = strippedBody !== undefined ? strippedBody.trim() : ''; + + // Ignore spaces in body text. Emojis with spaces in between should + // still be counted as purely emoji messages. + contentBodyTrimmed = contentBodyTrimmed.replace(WHITESPACE_REGEX, ''); + + // Remove zero width joiner characters from emoji messages. This ensures + // that emojis that are made up of multiple unicode characters are still + // presented as large. + contentBodyTrimmed = contentBodyTrimmed.replace(ZWJ_REGEX, ''); + + const match = BIGEMOJI_REGEX.exec(contentBodyTrimmed); + emojiBody = match && match[0] && match[0].length === contentBodyTrimmed.length && + // Prevent user pills expanding for users with only emoji in + // their username + ( + content.formatted_body == undefined || + !content.formatted_body.includes("https://matrix.to/") + ); } const className = classNames({ 'mx_EventTile_body': true, 'mx_EventTile_bigEmoji': emojiBody, - 'markdown-body': isHtmlMessage, + 'markdown-body': isHtmlMessage && !emojiBody, }); return isDisplayedWithHtml ? - : - { strippedBody }; + : + { strippedBody }; } -export function emojifyText(text) { - return { - __html: unicodeToImage(escape(text)), - }; +/** + * Linkifies the given string. This is a wrapper around 'linkifyjs/string'. + * + * @param {string} str + * @returns {string} + */ +export function linkifyString(str) { + return _linkifyString(str); +} + +/** + * Linkifies the given DOM element. This is a wrapper around 'linkifyjs/element'. + * + * @param {object} element DOM element to linkify + * @param {object} [options] Options for linkifyElement. Default: linkifyMatrix.options + * @returns {object} + */ +export function linkifyElement(element, options = linkifyMatrix.options) { + return _linkifyElement(element, options); +} + +/** + * Linkify the given string and sanitize the HTML afterwards. + * + * @param {string} dirtyHtml The HTML string to sanitize and linkify + * @returns {string} + */ +export function linkifyAndSanitizeHtml(dirtyHtml) { + return sanitizeHtml(linkifyString(dirtyHtml), sanitizeHtmlParams); } diff --git a/src/IntegrationManager.js b/src/IntegrationManager.js index eb45a1f425..165ee6390d 100644 --- a/src/IntegrationManager.js +++ b/src/IntegrationManager.js @@ -51,6 +51,11 @@ export default class IntegrationManager { */ static async open(integName, integId, onFinished) { await IntegrationManager._init(); + if (global.mxIntegrationManager.client) { + await global.mxIntegrationManager.client.connect(); + } else { + return; + } const IntegrationsManager = sdk.getComponent("views.settings.IntegrationsManager"); if (global.mxIntegrationManager.error || !(global.mxIntegrationManager.client && global.mxIntegrationManager.client.hasCredentials())) { diff --git a/src/Lifecycle.js b/src/Lifecycle.js index 2ea8aa190a..b8494ce9f5 100644 --- a/src/Lifecycle.js +++ b/src/Lifecycle.js @@ -31,8 +31,9 @@ import Modal from './Modal'; import sdk from './index'; import ActiveWidgetStore from './stores/ActiveWidgetStore'; import PlatformPeg from "./PlatformPeg"; +import { sendLoginRequest } from "./Login"; +import * as StorageManager from './utils/StorageManager'; import SettingsStore from "./settings/SettingsStore"; -import {sendLoginRequest} from "./Login"; /** * Called at startup, to attempt to build a logged-in Matrix session. It tries @@ -103,13 +104,38 @@ export async function loadSession(opts) { return _registerAsGuest(guestHsUrl, guestIsUrl, defaultDeviceDisplayName); } - // fall back to login screen + // fall back to welcome screen return false; } catch (e) { + if (e instanceof AbortLoginAndRebuildStorage) { + // If we're aborting login because of a storage inconsistency, we don't + // need to show the general failure dialog. Instead, just go back to welcome. + return false; + } return _handleLoadSessionFailure(e); } } +/** + * Gets the user ID of the persisted session, if one exists. This does not validate + * that the user's credentials still work, just that they exist and that a user ID + * is associated with them. The session is not loaded. + * @returns {String} The persisted session's owner, if an owner exists. Null otherwise. + */ +export function getStoredSessionOwner() { + const {hsUrl, userId, accessToken} = _getLocalStorageSessionVars(); + return hsUrl && userId && accessToken ? userId : null; +} + +/** + * @returns {bool} True if the stored session is for a guest user or false if it is + * for a real user. If there is no stored session, return null. + */ +export function getStoredSessionIsGuest() { + const sessVars = _getLocalStorageSessionVars(); + return sessVars.hsUrl && sessVars.userId && sessVars.accessToken ? sessVars.isGuest : null; +} + /** * @param {Object} queryParams string->string map of the * query-parameters extracted from the real query-string of the starting @@ -187,9 +213,6 @@ export function handleInvalidStoreError(e) { function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { console.log(`Doing guest login on ${hsUrl}`); - // TODO: we should probably de-duplicate this and Login.loginAsGuest. - // Not really sure where the right home for it is. - // create a temporary MatrixClient to do the login const client = Matrix.createClient({ baseUrl: hsUrl, @@ -215,6 +238,24 @@ function _registerAsGuest(hsUrl, isUrl, defaultDeviceDisplayName) { }); } +function _getLocalStorageSessionVars() { + const hsUrl = localStorage.getItem("mx_hs_url"); + const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org'; + const accessToken = localStorage.getItem("mx_access_token"); + const userId = localStorage.getItem("mx_user_id"); + const deviceId = localStorage.getItem("mx_device_id"); + + let isGuest; + if (localStorage.getItem("mx_is_guest") !== null) { + isGuest = localStorage.getItem("mx_is_guest") === "true"; + } else { + // legacy key name + isGuest = localStorage.getItem("matrix-is-guest") === "true"; + } + + return {hsUrl, isUrl, accessToken, userId, deviceId, isGuest}; +} + // returns a promise which resolves to true if a session is found in // localstorage // @@ -229,19 +270,8 @@ async function _restoreFromLocalStorage() { if (!localStorage) { return false; } - const hsUrl = localStorage.getItem("mx_hs_url"); - const isUrl = localStorage.getItem("mx_is_url") || 'https://matrix.org'; - const accessToken = localStorage.getItem("mx_access_token"); - const userId = localStorage.getItem("mx_user_id"); - const deviceId = localStorage.getItem("mx_device_id"); - let isGuest; - if (localStorage.getItem("mx_is_guest") !== null) { - isGuest = localStorage.getItem("mx_is_guest") === "true"; - } else { - // legacy key name - isGuest = localStorage.getItem("matrix-is-guest") === "true"; - } + const {hsUrl, isUrl, accessToken, userId, deviceId, isGuest} = _getLocalStorageSessionVars(); if (accessToken && userId && hsUrl) { console.log(`Restoring session for ${userId}`); @@ -261,7 +291,7 @@ async function _restoreFromLocalStorage() { } function _handleLoadSessionFailure(e) { - console.log("Unable to load session", e); + console.error("Unable to load session", e); const def = Promise.defer(); const SessionRestoreErrorDialog = @@ -336,6 +366,22 @@ async function _doSetLoggedIn(credentials, clearStorage) { await _clearStorage(); } + const results = await StorageManager.checkConsistency(); + // If there's an inconsistency between account data in local storage and the + // crypto store, we'll be generally confused when handling encrypted data. + // Show a modal recommending a full reset of storage. + if (results.dataInLocalStorage && results.cryptoInited && !results.dataInCryptoStore) { + const signOut = await _showStorageEvictedDialog(); + if (signOut) { + await _clearStorage(); + // This error feels a bit clunky, but we want to make sure we don't go any + // further and instead head back to sign in. + throw new AbortLoginAndRebuildStorage( + "Aborting login in progress because of storage inconsistency", + ); + } + } + Analytics.setLoggedIn(credentials.guest, credentials.homeserverUrl, credentials.identityServerUrl); if (localStorage) { @@ -366,6 +412,19 @@ async function _doSetLoggedIn(credentials, clearStorage) { return MatrixClientPeg.get(); } +function _showStorageEvictedDialog() { + const StorageEvictedDialog = sdk.getComponent('views.dialogs.StorageEvictedDialog'); + return new Promise(resolve => { + Modal.createTrackedDialog('Storage evicted', '', StorageEvictedDialog, { + onFinished: resolve, + }); + }); +} + +// Note: Babel 6 requires the `transform-builtin-extend` plugin for this to satisfy +// `instanceof`. Babel 7 supports this natively in their class handling. +class AbortLoginAndRebuildStorage extends Error { } + function _persistCredentialsToLocalStorage(credentials) { localStorage.setItem("mx_hs_url", credentials.homeserverUrl); localStorage.setItem("mx_is_url", credentials.identityServerUrl); @@ -440,7 +499,7 @@ async function startMatrixClient() { dis.dispatch({action: 'will_start_client'}, true); Notifier.start(); - UserActivity.start(); + UserActivity.sharedInstance().start(); if (!SettingsStore.getValue("lowBandwidth")) { Presence.start(); } @@ -488,7 +547,7 @@ function _clearStorage() { */ export function stopMatrixClient() { Notifier.stop(); - UserActivity.stop(); + UserActivity.sharedInstance().stop(); Presence.stop(); ActiveWidgetStore.stop(); if (DMRoomMap.shared()) DMRoomMap.shared().stop(); diff --git a/src/Login.js b/src/Login.js index 893ec42097..c31a9308a8 100644 --- a/src/Login.js +++ b/src/Login.js @@ -81,26 +81,6 @@ export default class Login { return flowStep ? flowStep.type : null; } - loginAsGuest() { - const client = this._createTemporaryClient(); - return client.registerGuest({ - body: { - initial_device_display_name: this._defaultDeviceDisplayName, - }, - }).then((creds) => { - return { - userId: creds.user_id, - deviceId: creds.device_id, - accessToken: creds.access_token, - homeserverUrl: this._hsUrl, - identityServerUrl: this._isUrl, - guest: true, - }; - }, (error) => { - throw error; - }); - } - loginViaPassword(username, phoneCountry, phoneNumber, pass) { const self = this; diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 12301e3716..574f05bf85 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -30,6 +30,8 @@ import MatrixActionCreators from './actions/MatrixActionCreators'; import {phasedRollOutExpiredForUser} from "./PhasedRollOut"; import Modal from './Modal'; import {verificationMethods} from 'matrix-js-sdk/lib/crypto'; +import MatrixClientBackedSettingsHandler from "./settings/handlers/MatrixClientBackedSettingsHandler"; +import * as StorageManager from './utils/StorageManager'; interface MatrixClientCreds { homeserverUrl: string, @@ -102,7 +104,7 @@ class MatrixClientPeg { } catch (err) { if (dbType === 'indexeddb') { console.error('Error starting matrixclient store - falling back to memory store', err); - this.matrixClient.store = new Matrix.MatrixInMemoryStore({ + this.matrixClient.store = new Matrix.MemoryStore({ localStorage: global.localStorage, }); } else { @@ -112,14 +114,17 @@ class MatrixClientPeg { } } + StorageManager.trackStores(this.matrixClient); + // try to initialise e2e on the new client try { // check that we have a version of the js-sdk which includes initCrypto if (!SettingsStore.getValue("lowBandwidth") && this.matrixClient.initCrypto) { await this.matrixClient.initCrypto(); + StorageManager.setCryptoInitialised(true); } } catch (e) { - if (e.name === 'InvalidCryptoStoreError') { + if (e && e.name === 'InvalidCryptoStoreError') { // The js-sdk found a crypto DB too new for it to use const CryptoStoreTooNewDialog = sdk.getComponent("views.dialogs.CryptoStoreTooNewDialog"); @@ -129,23 +134,17 @@ class MatrixClientPeg { } // this can happen for a number of reasons, the most likely being // that the olm library was missing. It's not fatal. - console.warn("Unable to initialise e2e: " + e); + console.warn("Unable to initialise e2e", e); } const opts = utils.deepCopy(this.opts); // the react sdk doesn't work without this, so don't allow opts.pendingEventOrdering = "detached"; + opts.lazyLoadMembers = true; - const LAZY_LOADING_FEATURE = "feature_lazyloading"; - if (SettingsStore.isFeatureEnabled(LAZY_LOADING_FEATURE)) { - const userId = this.matrixClient.credentials.userId; - if (phasedRollOutExpiredForUser(userId, LAZY_LOADING_FEATURE, Date.now())) { - opts.lazyLoadMembers = true; - } - } - - // Connect the matrix client to the dispatcher + // Connect the matrix client to the dispatcher and setting handlers MatrixActionCreators.start(this.matrixClient); + MatrixClientBackedSettingsHandler.matrixClient = this.matrixClient; console.log(`MatrixClientPeg: really starting MatrixClient`); await this.get().startClient(opts); @@ -176,7 +175,10 @@ class MatrixClientPeg { return matches[1]; } - _createClient(creds: MatrixClientCreds, useIndexedDb) { + _createClient(creds: MatrixClientCreds) { + const aggregateRelations = SettingsStore.isFeatureEnabled("feature_reactions"); + const enableEdits = SettingsStore.isFeatureEnabled("feature_message_editing"); + const opts = { baseUrl: creds.homeserverUrl, idBaseUrl: creds.identityServerUrl, @@ -184,11 +186,12 @@ class MatrixClientPeg { userId: creds.userId, deviceId: creds.deviceId, timelineSupport: true, - forceTURN: !SettingsStore.getValue('webRtcForcePeerToPeer', false), - verificationMethods: [verificationMethods.SAS] + forceTURN: !SettingsStore.getValue('webRtcAllowPeerToPeer', false), + verificationMethods: [verificationMethods.SAS], + unstableClientRelationAggregation: aggregateRelations || enableEdits, }; - this.matrixClient = createMatrixClient(opts, useIndexedDb); + this.matrixClient = createMatrixClient(opts); // we're going to add eventlisteners for each matrix event tile, so the // potential number of event listeners is quite high. diff --git a/src/Modal.js b/src/Modal.js index 960e0e5c30..a114ad2d3c 100644 --- a/src/Modal.js +++ b/src/Modal.js @@ -26,6 +26,7 @@ import dis from './dispatcher'; import { _t } from './languageHandler'; const DIALOG_CONTAINER_ID = "mx_Dialog_Container"; +const STATIC_DIALOG_CONTAINER_ID = "mx_Dialog_StaticContainer"; /** * Wrap an asynchronous loader function with a react component which shows a @@ -74,10 +75,9 @@ const AsyncWrapper = React.createClass({ }, render: function() { - const {loader, ...otherProps} = this.props; if (this.state.component) { const Component = this.state.component; - return ; + return ; } else if (this.state.error) { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); @@ -106,7 +106,12 @@ class ModalManager { // this modal. Remove all other modals from the stack when this modal // is closed. this._priorityModal = null; + // The modal to keep open underneath other modals if possible. Useful + // for cases like Settings where the modal should remain open while the + // user is prompted for more information/errors. + this._staticModal = null; // A list of the modals we have stacked up, with the most recent at [0] + // Neither the static nor priority modal will be in this list. this._modals = [ /* { elem: React component for this dialog @@ -118,6 +123,10 @@ class ModalManager { this.closeAll = this.closeAll.bind(this); } + hasDialogs() { + return this._priorityModal || this._staticModal || this._modals.length > 0; + } + getOrCreateContainer() { let container = document.getElementById(DIALOG_CONTAINER_ID); @@ -130,13 +139,25 @@ class ModalManager { return container; } + getOrCreateStaticContainer() { + let container = document.getElementById(STATIC_DIALOG_CONTAINER_ID); + + if (!container) { + container = document.createElement("div"); + container.id = STATIC_DIALOG_CONTAINER_ID; + document.body.appendChild(container); + } + + return container; + } + createTrackedDialog(analyticsAction, analyticsInfo, ...rest) { Analytics.trackEvent('Modal', analyticsAction, analyticsInfo); return this.createDialog(...rest); } createDialog(Element, ...rest) { - return this.createDialogAsync(new Promise(resolve => resolve(Element)), ...rest); + return this.createDialogAsync(Promise.resolve(Element), ...rest); } createTrackedDialogAsync(analyticsAction, analyticsInfo, ...rest) { @@ -166,29 +187,40 @@ class ModalManager { * of other modals that are currently in the stack. * Also, when closed, all modals will be removed * from the stack. + * @param {boolean} isStaticModal if true, this modal will be displayed under other + * modals in the stack. When closed, all modals will + * also be removed from the stack. This is not compatible + * with being a priority modal. Only one modal can be + * static at a time. + * @returns {object} Object with 'close' parameter being a function that will close the dialog */ - createDialogAsync(prom, props, className, isPriorityModal) { - const self = this; + createDialogAsync(prom, props, className, isPriorityModal, isStaticModal) { const modal = {}; // never call this from onFinished() otherwise it will loop // - // nb explicit function() rather than arrow function, to get `arguments` - const closeDialog = function() { - if (props && props.onFinished) props.onFinished.apply(null, arguments); - const i = self._modals.indexOf(modal); + const closeDialog = (...args) => { + if (props && props.onFinished) props.onFinished.apply(null, args); + const i = this._modals.indexOf(modal); if (i >= 0) { - self._modals.splice(i, 1); + this._modals.splice(i, 1); } - if (self._priorityModal === modal) { - self._priorityModal = null; + if (this._priorityModal === modal) { + this._priorityModal = null; // XXX: This is destructive - self._modals = []; + this._modals = []; } - self._reRender(); + if (this._staticModal === modal) { + this._staticModal = null; + + // XXX: This is destructive + this._modals = []; + } + + this._reRender(); }; // don't attempt to reuse the same AsyncWrapper for different dialogs, @@ -207,6 +239,9 @@ class ModalManager { if (isPriorityModal) { // XXX: This is destructive this._priorityModal = modal; + } else if (isStaticModal) { + // This is intentionally destructive + this._staticModal = modal; } else { this._modals.unshift(modal); } @@ -216,12 +251,18 @@ class ModalManager { } closeAll() { - const modals = this._modals; + const modalsToClose = [...this._modals, this._priorityModal]; this._modals = []; + this._priorityModal = null; - for (let i = 0; i < modals.length; i++) { - const m = modals[i]; - if (m.onFinished) { + if (this._staticModal && modalsToClose.length === 0) { + modalsToClose.push(this._staticModal); + this._staticModal = null; + } + + for (let i = 0; i < modalsToClose.length; i++) { + const m = modalsToClose[i]; + if (m && m.onFinished) { m.onFinished(false); } } @@ -230,13 +271,14 @@ class ModalManager { } _reRender() { - if (this._modals.length == 0 && !this._priorityModal) { + if (this._modals.length === 0 && !this._priorityModal && !this._staticModal) { // If there is no modal to render, make all of Riot available // to screen reader users again dis.dispatch({ action: 'aria_unhide_main_app', }); ReactDOM.unmountComponentAtNode(this.getOrCreateContainer()); + ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer()); return; } @@ -247,17 +289,45 @@ class ModalManager { action: 'aria_hide_main_app', }); - const modal = this._priorityModal ? this._priorityModal : this._modals[0]; - const dialog = ( -
-
- { modal.elem } -
-
-
- ); + if (this._staticModal) { + const classes = "mx_Dialog_wrapper mx_Dialog_staticWrapper " + + (this._staticModal.className ? this._staticModal.className : ''); - ReactDOM.render(dialog, this.getOrCreateContainer()); + const staticDialog = ( +
+
+ { this._staticModal.elem } +
+
+
+ ); + + ReactDOM.render(staticDialog, this.getOrCreateStaticContainer()); + } else { + // This is safe to call repeatedly if we happen to do that + ReactDOM.unmountComponentAtNode(this.getOrCreateStaticContainer()); + } + + const modal = this._priorityModal ? this._priorityModal : this._modals[0]; + if (modal) { + const classes = "mx_Dialog_wrapper " + + (this._staticModal ? "mx_Dialog_wrapperWithStaticUnder " : '') + + (modal.className ? modal.className : ''); + + const dialog = ( +
+
+ {modal.elem} +
+
+
+ ); + + ReactDOM.render(dialog, this.getOrCreateContainer()); + } else { + // This is safe to call repeatedly if we happen to do that + ReactDOM.unmountComponentAtNode(this.getOrCreateContainer()); + } } } diff --git a/src/Notifier.js b/src/Notifier.js index 80e8be1084..6a4f9827f7 100644 --- a/src/Notifier.js +++ b/src/Notifier.js @@ -220,7 +220,17 @@ const Notifier = { } }, - isToolbarHidden: function() { + shouldShowToolbar: function() { + const client = MatrixClientPeg.get(); + if (!client) { + return false; + } + const isGuest = client.isGuest(); + return !isGuest && this.supportsDesktopNotifications() && + !this.isEnabled() && !this._isToolbarHidden(); + }, + + _isToolbarHidden: function() { // Check localStorage for any such meta data if (global.localStorage) { return global.localStorage.getItem("notifications_hidden") === "true"; diff --git a/src/PageTypes.js b/src/PageTypes.js index 60111723fb..09e0eadbd7 100644 --- a/src/PageTypes.js +++ b/src/PageTypes.js @@ -19,7 +19,6 @@ limitations under the License. export default { HomePage: "home_page", RoomView: "room_view", - UserSettings: "user_settings", RoomDirectory: "room_directory", UserView: "user_view", GroupView: "group_view", diff --git a/src/Registration.js b/src/Registration.js index f3a2076ed6..42e172ca0b 100644 --- a/src/Registration.js +++ b/src/Registration.js @@ -35,8 +35,10 @@ export const SAFE_LOCALPART_REGEX = /^[a-z0-9=_\-./]+$/; * on what the HS supports * * @param {object} options - * @param {bool} options.go_home_on_cancel If true, goes to - * the hame page if the user cancels the action + * @param {bool} options.go_home_on_cancel + * If true, goes to the home page if the user cancels the action + * @param {bool} options.go_welcome_on_cancel + * If true, goes to the welcome page if the user cancels the action */ export async function startAnyRegistrationFlow(options) { if (options === undefined) options = {}; @@ -73,6 +75,8 @@ export async function startAnyRegistrationFlow(options) { dis.dispatch({action: 'start_registration'}); } else if (options.go_home_on_cancel) { dis.dispatch({action: 'view_home_page'}); + } else if (options.go_welcome_on_cancel) { + dis.dispatch({action: 'view_welcome_page'}); } }, }); diff --git a/src/RichText.js b/src/RichText.js deleted file mode 100644 index 3e8f834da6..0000000000 --- a/src/RichText.js +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2015 - 2017 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import * as emojione from 'emojione'; - - -export function unicodeToEmojiUri(str) { - const mappedUnicode = emojione.mapUnicodeToShort(); - - // remove any zero width joiners/spaces used in conjugate emojis as the emojione URIs don't contain them - return str.replace(emojione.regUnicode, function(unicodeChar) { - if ((typeof unicodeChar === 'undefined') || (unicodeChar === '') || (!(unicodeChar in emojione.jsEscapeMap))) { - // if the unicodeChar doesn't exist just return the entire match - return unicodeChar; - } else { - // get the unicode codepoint from the actual char - const unicode = emojione.jsEscapeMap[unicodeChar]; - - const short = mappedUnicode[unicode]; - const fname = emojione.emojioneList[short].fname; - - return emojione.imagePathSVG+fname+'.svg'+emojione.cacheBustParam; - } - }); -} diff --git a/src/RoomInvite.js b/src/RoomInvite.js index 3547b9195f..34b9635780 100644 --- a/src/RoomInvite.js +++ b/src/RoomInvite.js @@ -45,7 +45,7 @@ export function showStartChatInviteDialog() { Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, { title: _t('Start a chat'), description: _t("Who would you like to communicate with?"), - placeholder: _t("Email, name or matrix ID"), + placeholder: _t("Email, name or Matrix ID"), validAddressTypes: ['mx-user-id', 'email'], button: _t("Start Chat"), onFinished: _onStartChatFinished, @@ -58,13 +58,31 @@ export function showRoomInviteDialog(roomId) { title: _t('Invite new room members'), description: _t('Who would you like to add to this room?'), button: _t('Send Invites'), - placeholder: _t("Email, name or matrix ID"), + placeholder: _t("Email, name or Matrix ID"), onFinished: (shouldInvite, addrs) => { _onRoomInviteFinished(roomId, shouldInvite, addrs); }, }); } +/** + * Checks if the given MatrixEvent is a valid 3rd party user invite. + * @param {MatrixEvent} event The event to check + * @returns {boolean} True if valid, false otherwise + */ +export function isValid3pidInvite(event) { + if (!event || event.getType() !== "m.room.third_party_invite") return false; + + // any events without these keys are not valid 3pid invites, so we ignore them + const requiredKeys = ['key_validity_url', 'public_key', 'display_name']; + for (let i = 0; i < requiredKeys.length; ++i) { + if (!event.getContent()[requiredKeys[i]]) return false; + } + + // Valid enough by our standards + return true; +} + function _onStartChatFinished(shouldInvite, addrs) { if (!shouldInvite) return; diff --git a/src/RoomNotifs.js b/src/RoomNotifs.js index 91e49fe09b..39384b5bea 100644 --- a/src/RoomNotifs.js +++ b/src/RoomNotifs.js @@ -23,6 +23,49 @@ export const ALL_MESSAGES = 'all_messages'; export const MENTIONS_ONLY = 'mentions_only'; export const MUTE = 'mute'; +export const BADGE_STATES = [ALL_MESSAGES, ALL_MESSAGES_LOUD]; +export const MENTION_BADGE_STATES = [...BADGE_STATES, MENTIONS_ONLY]; + +function _shouldShowNotifBadge(roomNotifState) { + const showBadgeInStates = [ALL_MESSAGES, ALL_MESSAGES_LOUD]; + return showBadgeInStates.indexOf(roomNotifState) > -1; +} + +function _shouldShowMentionBadge(roomNotifState) { + return roomNotifState !== MUTE; +} + +export function aggregateNotificationCount(rooms) { + return rooms.reduce((result, room, index) => { + const roomNotifState = getRoomNotifsState(room.roomId); + const highlight = room.getUnreadNotificationCount('highlight') > 0; + const notificationCount = room.getUnreadNotificationCount(); + + const notifBadges = notificationCount > 0 && _shouldShowNotifBadge(roomNotifState); + const mentionBadges = highlight && _shouldShowMentionBadge(roomNotifState); + const badges = notifBadges || mentionBadges; + + if (badges) { + result.count += notificationCount; + if (highlight) { + result.highlight = true; + } + } + return result; + }, {count: 0, highlight: false}); +} + +export function getRoomHasBadge(room) { + const roomNotifState = getRoomNotifsState(room.roomId); + const highlight = room.getUnreadNotificationCount('highlight') > 0; + const notificationCount = room.getUnreadNotificationCount(); + + const notifBadges = notificationCount > 0 && _shouldShowNotifBadge(roomNotifState); + const mentionBadges = highlight && _shouldShowMentionBadge(roomNotifState); + + return notifBadges || mentionBadges; +} + export function getRoomNotifsState(roomId) { if (MatrixClientPeg.get().isGuest()) return ALL_MESSAGES; @@ -66,6 +109,28 @@ export function setRoomNotifsState(roomId, newState) { } } +export function getUnreadNotificationCount(room, type=null) { + let notificationCount = room.getUnreadNotificationCount(type); + + // Check notification counts in the old room just in case there's some lost + // there. We only go one level down to avoid performance issues, and theory + // is that 1st generation rooms will have already been read by the 3rd generation. + const createEvent = room.currentState.getStateEvents("m.room.create", ""); + if (createEvent && createEvent.getContent()['predecessor']) { + const oldRoomId = createEvent.getContent()['predecessor']['room_id']; + const oldRoom = MatrixClientPeg.get().getRoom(oldRoomId); + if (oldRoom) { + // We only ever care if there's highlights in the old room. No point in + // notifying the user for unread messages because they would have extreme + // difficulty changing their notification preferences away from "All Messages" + // and "Noisy". + notificationCount += oldRoom.getUnreadNotificationCount("highlight"); + } + } + + return notificationCount; +} + function setRoomNotifsStateMuted(roomId) { const cli = MatrixClientPeg.get(); const promises = []; @@ -163,4 +228,3 @@ function isRuleForRoom(roomId, rule) { function isMuteRule(rule) { return (rule.actions.length === 1 && rule.actions[0] === 'dont_notify'); } - diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 0639e7ceae..24979aff65 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -21,6 +21,9 @@ const request = require('browser-request'); const SdkConfig = require('./SdkConfig'); const MatrixClientPeg = require('./MatrixClientPeg'); +// The version of the integration manager API we're intending to work with +const imApiVersion = "1.1"; + class ScalarAuthClient { constructor() { this.scalarToken = null; @@ -66,7 +69,7 @@ class ScalarAuthClient { request({ method: "GET", uri: url, - qs: {scalar_token: token}, + qs: {scalar_token: token, v: imApiVersion}, json: true, }, (err, response, body) => { if (err) { @@ -100,6 +103,7 @@ class ScalarAuthClient { request({ method: 'POST', uri: scalar_rest_url+'/register', + qs: {v: imApiVersion}, body: openid_token_object, json: true, }, (err, response, body) => { diff --git a/src/SdkConfig.js b/src/SdkConfig.js index 65982bd6f2..eb18dad453 100644 --- a/src/SdkConfig.js +++ b/src/SdkConfig.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -const DEFAULTS = { +export const DEFAULTS = { // URL to a page we show in an iframe to configure integrations integrations_ui_url: "https://scalar.vector.im/", // Base URL to the REST interface of the integrations server @@ -41,6 +41,12 @@ class SdkConfig { static unset() { global.mxReactSdkConfig = undefined; } + + static add(cfg) { + const liveConfig = SdkConfig.get(); + const newConfig = Object.assign({}, liveConfig, cfg); + SdkConfig.put(newConfig); + } } module.exports = SdkConfig; diff --git a/src/SlashCommands.js b/src/SlashCommands.js index b9fc4e4334..f25bc9af07 100644 --- a/src/SlashCommands.js +++ b/src/SlashCommands.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2018 New Vector Ltd +Copyright 2019 Michael Telatynski <7t3chguy@gmail.com> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -27,7 +28,11 @@ import SettingsStore, {SettingLevel} from './settings/SettingsStore'; import {MATRIXTO_URL_PATTERN} from "./linkify-matrix"; import * as querystring from "querystring"; import MultiInviter from './utils/MultiInviter'; - +import { linkifyAndSanitizeHtml } from './HtmlUtils'; +import QuestionDialog from "./components/views/dialogs/QuestionDialog"; +import WidgetUtils from "./utils/WidgetUtils"; +import {textToHtmlRainbow} from "./utils/colour"; +import Promise from "bluebird"; class Command { constructor({name, args='', description, runFn, hideCompletionAfterSpace=false}) { @@ -70,6 +75,19 @@ function success(promise) { /* eslint-disable babel/no-invalid-this */ export const CommandMap = { + shrug: new Command({ + name: 'shrug', + args: '', + description: _td('Prepends ¯\\_(ツ)_/¯ to a plain-text message'), + runFn: function(roomId, args) { + let message = '¯\\_(ツ)_/¯'; + if (args) { + message = message + ' ' + args; + } + return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); + }, + }), + ddg: new Command({ name: 'ddg', args: '', @@ -92,7 +110,72 @@ export const CommandMap = { description: _td('Upgrades a room to a new version'), runFn: function(roomId, args) { if (args) { - return success(MatrixClientPeg.get().upgradeRoom(roomId, args)); + const room = MatrixClientPeg.get().getRoom(roomId); + Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation', + QuestionDialog, { + title: _t('Room upgrade confirmation'), + description: ( +
+

{_t("Upgrading a room can be destructive and isn't always necessary.")}

+

+ {_t( + "Room upgrades are usually recommended when a room version is considered " + + "unstable. Unstable room versions might have bugs, missing features, or " + + "security vulnerabilities.", + {}, { + "i": (sub) => {sub}, + }, + )} +

+

+ {_t( + "Room upgrades usually only affect server-side processing of the " + + "room. If you're having problems with your Riot client, please file an issue " + + "with .", + {}, { + "i": (sub) => {sub}, + "issueLink": () => { + return + https://github.com/vector-im/riot-web/issues/new/choose + ; + }, + }, + )} +

+

+ {_t( + "Warning: Upgrading a room will not automatically migrate room " + + "members to the new version of the room. We'll post a link to the new room " + + "in the old version of the room - room members will have to click this link to " + + "join the new room.", + {}, { + "b": (sub) => {sub}, + "i": (sub) => {sub}, + }, + )} +

+

+ {_t( + "Please confirm that you'd like to go forward with upgrading this room " + + "from to .", + {}, + { + oldVersion: () => {room ? room.getVersion() : "1"}, + newVersion: () => {args}, + }, + )} +

+
+ ), + button: _t("Upgrade"), + onFinished: (confirm) => { + if (!confirm) return; + + MatrixClientPeg.get().upgradeRoom(roomId, args); + }, + }); + return success(); } return reject(this.getUsage()); }, @@ -110,6 +193,65 @@ export const CommandMap = { }, }), + myroomnick: new Command({ + name: 'myroomnick', + args: '', + description: _td('Changes your display nickname in the current room only'), + runFn: function(roomId, args) { + if (args) { + const cli = MatrixClientPeg.get(); + const ev = cli.getRoom(roomId).currentState.getStateEvents('m.room.member', cli.getUserId()); + const content = { + ...ev ? ev.getContent() : { membership: 'join' }, + displayname: args, + }; + return success(cli.sendStateEvent(roomId, 'm.room.member', content, cli.getUserId())); + } + return reject(this.getUsage()); + }, + }), + + myroomavatar: new Command({ + name: 'myroomavatar', + args: '[]', + description: _td('Changes your avatar in this current room only'), + runFn: function(roomId, args) { + const cli = MatrixClientPeg.get(); + const room = cli.getRoom(roomId); + const userId = cli.getUserId(); + + let promise = Promise.resolve(args); + if (!args) { + promise = new Promise((resolve) => { + const fileSelector = document.createElement('input'); + fileSelector.setAttribute('type', 'file'); + fileSelector.onchange = (ev) => { + const file = ev.target.files[0]; + + const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog"); + Modal.createTrackedDialog('Upload Files confirmation', '', UploadConfirmDialog, { + file, + onFinished: (shouldContinue) => { + if (shouldContinue) resolve(cli.uploadContent(file)); + }, + }); + }; + + fileSelector.click(); + }); + } + + return success(promise.then((url) => { + const ev = room.currentState.getStateEvents('m.room.member', userId); + const content = { + ...ev ? ev.getContent() : { membership: 'join' }, + avatar_url: url, + }; + return cli.sendStateEvent(roomId, 'm.room.member', content, userId); + })); + }, + }), + tint: new Command({ name: 'tint', args: ' []', @@ -137,13 +279,26 @@ export const CommandMap = { topic: new Command({ name: 'topic', - args: '', - description: _td('Sets the room topic'), + args: '[]', + description: _td('Gets or sets the room topic'), runFn: function(roomId, args) { + const cli = MatrixClientPeg.get(); if (args) { - return success(MatrixClientPeg.get().setRoomTopic(roomId, args)); + return success(cli.setRoomTopic(roomId, args)); } - return reject(this.getUsage()); + const room = cli.getRoom(roomId); + if (!room) return reject('Bad room ID: ' + roomId); + + const topicEvents = room.currentState.getStateEvents('m.room.topic', ''); + const topic = topicEvents && topicEvents.getContent().topic; + const topicHtml = topic ? linkifyAndSanitizeHtml(topic) : _t('This room has no topic.'); + + const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); + Modal.createTrackedDialog('Slash Commands', 'Topic', InfoDialog, { + title: room.name, + description:
, + }); + return success(); }, }), @@ -321,7 +476,7 @@ export const CommandMap = { if (!targetRoomId) targetRoomId = roomId; return success( - cli.leave(targetRoomId).then(function() { + cli.leaveRoomChain(targetRoomId).then(function() { dis.dispatch({action: 'view_next_room'}); }), ); @@ -363,7 +518,7 @@ export const CommandMap = { unban: new Command({ name: 'unban', args: '', - description: _td('Unbans user with given id'), + description: _td('Unbans user with given ID'), runFn: function(roomId, args) { if (args) { const matches = args.match(/^(\S+)$/); @@ -391,13 +546,12 @@ export const CommandMap = { ignoredUsers.push(userId); // de-duped internally in the js-sdk return success( cli.setIgnoredUsers(ignoredUsers).then(() => { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, { + const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); + Modal.createTrackedDialog('Slash Commands', 'User ignored', InfoDialog, { title: _t('Ignored user'), description:

{ _t('You are now ignoring %(userId)s', {userId}) }

, - hasCancelButton: false, }); }), ); @@ -423,13 +577,12 @@ export const CommandMap = { if (index !== -1) ignoredUsers.splice(index, 1); return success( cli.setIgnoredUsers(ignoredUsers).then(() => { - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, { + const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); + Modal.createTrackedDialog('Slash Commands', 'User unignored', InfoDialog, { title: _t('Unignored user'), description:

{ _t('You are no longer ignoring %(userId)s', {userId}) }

, - hasCancelButton: false, }); }), ); @@ -498,6 +651,26 @@ export const CommandMap = { }, }), + addwidget: new Command({ + name: 'addwidget', + args: '', + description: _td('Adds a custom widget by URL to the room'), + runFn: function(roomId, args) { + if (!args || (!args.startsWith("https://") && !args.startsWith("http://"))) { + return reject(_t("Please supply a https:// or http:// widget URL")); + } + if (WidgetUtils.canUserModifyWidgets(roomId)) { + const userId = MatrixClientPeg.get().getUserId(); + const nowMs = (new Date()).getTime(); + const widgetId = encodeURIComponent(`${roomId}_${userId}_${nowMs}`); + return success(WidgetUtils.setRoomWidget( + roomId, widgetId, "m.custom", args, "Custom Widget", {})); + } else { + return reject(_t("You cannot modify widgets in this room.")); + } + }, + }), + // Verify a user, device, and pubkey tuple verify: new Command({ name: 'verify', @@ -546,8 +719,8 @@ export const CommandMap = { return cli.setDeviceVerified(userId, deviceId, true); }).then(() => { // Tell the user we verified everything - const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); - Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, { + const InfoDialog = sdk.getComponent('dialogs.InfoDialog'); + Modal.createTrackedDialog('Slash Commands', 'Verified key', InfoDialog, { title: _t('Verified key'), description:

@@ -558,7 +731,6 @@ export const CommandMap = { }

, - hasCancelButton: false, }); }), ); @@ -590,6 +762,26 @@ export const CommandMap = { return success(); }, }), + + rainbow: new Command({ + name: "rainbow", + description: _td("Sends the given message coloured as a rainbow"), + args: '', + runFn: function(roomId, args) { + if (!args) return reject(this.getUserId()); + return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); + }, + }), + + rainbowme: new Command({ + name: "rainbowme", + description: _td("Sends the given emote coloured as a rainbow"), + args: '', + runFn: function(roomId, args) { + if (!args) return reject(this.getUserId()); + return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); + }, + }), }; /* eslint-enable babel/no-invalid-this */ @@ -599,6 +791,7 @@ const aliases = { j: "join", newballsplease: "discardsession", goto: "join", // because it handles event permalinks magically + roomnick: "myroomnick", }; diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 2a37295f83..a700fe2a3c 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -17,6 +17,7 @@ import MatrixClientPeg from './MatrixClientPeg'; import CallHandler from './CallHandler'; import { _t } from './languageHandler'; import * as Roles from './Roles'; +import {isValid3pidInvite} from "./RoomInvite"; function textForMemberEvent(ev) { // XXX: SYJS-16 "sender is sometimes null for join messages" @@ -134,6 +135,68 @@ function textForTombstoneEvent(ev) { return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); } +function textForJoinRulesEvent(ev) { + 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}); + case "invite": + 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', { + senderDisplayName, + rule: ev.getContent().join_rule, + }); + } +} + +function textForGuestAccessEvent(ev) { + 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}); + case "forbidden": + 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', { + senderDisplayName, + rule: ev.getContent().guest_access, + }); + } +} + +function textForRelatedGroupsEvent(ev) { + const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); + const groups = ev.getContent().groups || []; + const prevGroups = ev.getPrevContent().groups || []; + const added = groups.filter((g) => !prevGroups.includes(g)); + 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.', { + senderDisplayName, + groups: added.join(', '), + }); + } else if (!added.length && removed.length) { + 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 ' + + '%(oldGroups)s in this room.', { + senderDisplayName, + newGroups: added.join(', '), + oldGroups: removed.join(', '), + }); + } else { + // Don't bother rendering this change (because there were no changes) + return ''; + } +} + function textForServerACLEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const prevContent = ev.getPrevContent(); @@ -304,6 +367,15 @@ function textForCallInviteEvent(event) { function textForThreePidInviteEvent(event) { 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.', { + senderName, + targetDisplayName, + }); + } + return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', { senderName, targetDisplayName: event.getContent().display_name, @@ -439,6 +511,9 @@ const stateHandlers = { 'm.room.pinned_events': textForPinnedEvent, 'm.room.server_acl': textForServerACLEvent, 'm.room.tombstone': textForTombstoneEvent, + 'm.room.join_rules': textForJoinRulesEvent, + 'm.room.guest_access': textForGuestAccessEvent, + 'm.room.related_groups': textForRelatedGroupsEvent, 'im.vector.modular.widgets': textForWidgetEvent, }; diff --git a/src/Unread.js b/src/Unread.js index 55e60f2a9a..9514ec821b 100644 --- a/src/Unread.js +++ b/src/Unread.js @@ -32,7 +32,7 @@ module.exports = { return false; } else if (ev.getType() == 'm.call.answer' || ev.getType() == 'm.call.hangup') { return false; - } else if (ev.getType == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { + } else if (ev.getType() == 'm.room.message' && ev.getContent().msgtype == 'm.notify') { return false; } const EventTile = sdk.getComponent('rooms.EventTile'); diff --git a/src/UserActivity.js b/src/UserActivity.js index 145b23e36e..0d1b4d0cc0 100644 --- a/src/UserActivity.js +++ b/src/UserActivity.js @@ -1,5 +1,6 @@ /* Copyright 2015, 2016 OpenMarket Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,110 +18,173 @@ limitations under the License. import dis from './dispatcher'; import Timer from './utils/Timer'; -// important this is larger than the timeouts of timers -// used with UserActivity.timeWhileActive, -// such as READ_MARKER_INVIEW_THRESHOLD_MS, -// READ_MARKER_OUTOFVIEW_THRESHOLD_MS, -// READ_RECEIPT_INTERVAL_MS in TimelinePanel -const CURRENTLY_ACTIVE_THRESHOLD_MS = 2 * 60 * 1000; +// important these are larger than the timeouts of timers +// used with UserActivity.timeWhileActive*, +// such as READ_MARKER_INVIEW_THRESHOLD_MS (timeWhileActiveRecently), +// READ_MARKER_OUTOFVIEW_THRESHOLD_MS (timeWhileActiveRecently), +// READ_RECEIPT_INTERVAL_MS (timeWhileActiveNow) in TimelinePanel + +// 'Under a few seconds'. Must be less than 'RECENTLY_ACTIVE_THRESHOLD_MS' +const CURRENTLY_ACTIVE_THRESHOLD_MS = 700; + +// 'Under a few minutes'. +const RECENTLY_ACTIVE_THRESHOLD_MS = 2 * 60 * 1000; /** * This class watches for user activity (moving the mouse or pressing a key) * and starts/stops attached timers while the user is active. + * + * There are two classes of 'active': 'active now' and 'active recently' + * see doc on the userActive* functions for what these mean. */ -class UserActivity { - constructor() { - this._attachedTimers = []; - this._activityTimeout = new Timer(CURRENTLY_ACTIVE_THRESHOLD_MS); +export default class UserActivity { + constructor(windowObj, documentObj) { + this._window = windowObj; + this._document = documentObj; + + this._attachedActiveNowTimers = []; + this._attachedActiveRecentlyTimers = []; + this._activeNowTimeout = new Timer(CURRENTLY_ACTIVE_THRESHOLD_MS); + this._activeRecentlyTimeout = new Timer(RECENTLY_ACTIVE_THRESHOLD_MS); this._onUserActivity = this._onUserActivity.bind(this); - this._onDocumentBlurred = this._onDocumentBlurred.bind(this); + this._onWindowBlurred = this._onWindowBlurred.bind(this); this._onPageVisibilityChanged = this._onPageVisibilityChanged.bind(this); this.lastScreenX = 0; this.lastScreenY = 0; } + static sharedInstance() { + if (global.mxUserActivity === undefined) { + global.mxUserActivity = new UserActivity(window, document); + } + return global.mxUserActivity; + } + /** - * Runs the given timer while the user is active, aborting when the user becomes inactive. + * Runs the given timer while the user is 'active now', aborting when the user is no longer + * considered currently active. + * See userActiveNow() for what it means for a user to be 'active'. * Can be called multiple times with the same already running timer, which is a NO-OP. * Can be called before the user becomes active, in which case it is only started * later on when the user does become active. * @param {Timer} timer the timer to use */ - timeWhileActive(timer) { + timeWhileActiveNow(timer) { + this._timeWhile(timer, this._attachedActiveNowTimers); + if (this.userActiveNow()) { + timer.start(); + } + } + + /** + * Runs the given timer while the user is 'active' now or recently, + * aborting when the user becomes inactive. + * See userActiveRecently() for what it means for a user to be 'active recently'. + * Can be called multiple times with the same already running timer, which is a NO-OP. + * Can be called before the user becomes active, in which case it is only started + * later on when the user does become active. + * @param {Timer} timer the timer to use + */ + timeWhileActiveRecently(timer) { + this._timeWhile(timer, this._attachedActiveRecentlyTimers); + if (this.userActiveRecently()) { + timer.start(); + } + } + + _timeWhile(timer, attachedTimers) { // important this happens first - const index = this._attachedTimers.indexOf(timer); + const index = attachedTimers.indexOf(timer); if (index === -1) { - this._attachedTimers.push(timer); + attachedTimers.push(timer); // remove when done or aborted timer.finished().finally(() => { - const index = this._attachedTimers.indexOf(timer); + const index = attachedTimers.indexOf(timer); if (index !== -1) { // should never be -1 - this._attachedTimers.splice(index, 1); + attachedTimers.splice(index, 1); } // as we fork the promise here, // avoid unhandled rejection warnings }).catch((err) => {}); } - if (this.userCurrentlyActive()) { - timer.start(); - } } /** * Start listening to user activity */ start() { - document.onmousedown = this._onUserActivity; - document.onmousemove = this._onUserActivity; - document.onkeydown = this._onUserActivity; - document.addEventListener("visibilitychange", this._onPageVisibilityChanged); - document.addEventListener("blur", this._onDocumentBlurred); - document.addEventListener("focus", this._onUserActivity); + this._document.addEventListener('mousedown', this._onUserActivity); + this._document.addEventListener('mousemove', this._onUserActivity); + this._document.addEventListener('keydown', this._onUserActivity); + this._document.addEventListener("visibilitychange", this._onPageVisibilityChanged); + this._window.addEventListener("blur", this._onWindowBlurred); + this._window.addEventListener("focus", this._onUserActivity); // can't use document.scroll here because that's only the document // itself being scrolled. Need to use addEventListener's useCapture. // also this needs to be the wheel event, not scroll, as scroll is // fired when the view scrolls down for a new message. - window.addEventListener('wheel', this._onUserActivity, - { passive: true, capture: true }); + this._window.addEventListener('wheel', this._onUserActivity, { + passive: true, capture: true, + }); } /** * Stop tracking user activity */ stop() { - document.onmousedown = undefined; - document.onmousemove = undefined; - document.onkeydown = undefined; - window.removeEventListener('wheel', this._onUserActivity, - { passive: true, capture: true }); + this._document.removeEventListener('mousedown', this._onUserActivity); + this._document.removeEventListener('mousemove', this._onUserActivity); + this._document.removeEventListener('keydown', this._onUserActivity); + this._window.removeEventListener('wheel', this._onUserActivity, { + passive: true, capture: true, + }); - document.removeEventListener("visibilitychange", this._onPageVisibilityChanged); - document.removeEventListener("blur", this._onDocumentBlurred); - document.removeEventListener("focus", this._onUserActivity); + this._document.removeEventListener("visibilitychange", this._onPageVisibilityChanged); + this._window.removeEventListener("blur", this._onWindowBlurred); + this._window.removeEventListener("focus", this._onUserActivity); } /** - * Return true if there has been user activity very recently - * (ie. within a few seconds) - * @returns {boolean} true if user is currently/very recently active + * Return true if the user is currently 'active' + * A user is 'active' while they are interacting with the app and for a very short (<1s) + * time after that. This is intended to give a strong indication that the app has the + * user's attention at any given moment. + * @returns {boolean} true if user is currently 'active' */ - userCurrentlyActive() { - return this._activityTimeout.isRunning(); + userActiveNow() { + return this._activeNowTimeout.isRunning(); + } + + /** + * Return true if the user is currently active or has been recently + * A user is 'active recently' for a longer period of time (~2 mins) after + * they have been 'active' and while the app still has the focus. This is + * intended to indicate when the app may still have the user's attention + * (or they may have gone to make tea and left the window focused). + * @returns {boolean} true if user has been active recently + */ + userActiveRecently() { + return this._activeRecentlyTimeout.isRunning(); } _onPageVisibilityChanged(e) { - if (document.visibilityState === "hidden") { - this._activityTimeout.abort(); + if (this._document.visibilityState === "hidden") { + this._activeNowTimeout.abort(); + this._activeRecentlyTimeout.abort(); } else { this._onUserActivity(e); } } - _onDocumentBlurred() { - this._activityTimeout.abort(); + _onWindowBlurred() { + this._activeNowTimeout.abort(); + this._activeRecentlyTimeout.abort(); } - async _onUserActivity(event) { + _onUserActivity(event) { + // ignore anything if the window isn't focused + if (!this._document.hasFocus()) return; + if (event.screenX && event.type === "mousemove") { if (event.screenX === this.lastScreenX && event.screenY === this.lastScreenY) { // mouse hasn't actually moved @@ -131,19 +195,29 @@ class UserActivity { } dis.dispatch({action: 'user_activity'}); - if (!this._activityTimeout.isRunning()) { - this._activityTimeout.start(); + if (!this._activeNowTimeout.isRunning()) { + this._activeNowTimeout.start(); dis.dispatch({action: 'user_activity_start'}); - this._attachedTimers.forEach((t) => t.start()); - try { - await this._activityTimeout.finished(); - } catch (_e) { /* aborted */ } - this._attachedTimers.forEach((t) => t.abort()); + + this._runTimersUntilTimeout(this._attachedActiveNowTimers, this._activeNowTimeout); } else { - this._activityTimeout.restart(); + this._activeNowTimeout.restart(); + } + + if (!this._activeRecentlyTimeout.isRunning()) { + this._activeRecentlyTimeout.start(); + + this._runTimersUntilTimeout(this._attachedActiveRecentlyTimers, this._activeRecentlyTimeout); + } else { + this._activeRecentlyTimeout.restart(); } } + + async _runTimersUntilTimeout(attachedTimers, timeout) { + attachedTimers.forEach((t) => t.start()); + try { + await timeout.finished(); + } catch (_e) { /* aborted */ } + attachedTimers.forEach((t) => t.abort()); + } } - - -module.exports = new UserActivity(); diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js deleted file mode 100644 index b40d0529a2..0000000000 --- a/src/UserSettingsStore.js +++ /dev/null @@ -1,93 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import Promise from 'bluebird'; -import MatrixClientPeg from './MatrixClientPeg'; - -/* - * TODO: Make things use this. This is all WIP - see UserSettings.js for usage. - */ -export default { - loadProfileInfo: function() { - const cli = MatrixClientPeg.get(); - return cli.getProfileInfo(cli.credentials.userId); - }, - - saveDisplayName: function(newDisplayname) { - return MatrixClientPeg.get().setDisplayName(newDisplayname); - }, - - loadThreePids: function() { - if (MatrixClientPeg.get().isGuest()) { - return Promise.resolve({ - threepids: [], - }); // guests can't poke 3pid endpoint - } - return MatrixClientPeg.get().getThreePids(); - }, - - saveThreePids: function(threePids) { - // TODO - }, - - changePassword: function(oldPassword, newPassword) { - const cli = MatrixClientPeg.get(); - - const authDict = { - type: 'm.login.password', - user: cli.credentials.userId, - password: oldPassword, - }; - - return cli.setPassword(authDict, newPassword); - }, - - /* - * Returns the email pusher (pusher of type 'email') for a given - * email address. Email pushers all have the same app ID, so since - * pushers are unique over (app ID, pushkey), there will be at most - * one such pusher. - */ - getEmailPusher: function(pushers, address) { - if (pushers === undefined) { - return undefined; - } - for (let i = 0; i < pushers.length; ++i) { - if (pushers[i].kind === 'email' && pushers[i].pushkey === address) { - return pushers[i]; - } - } - return undefined; - }, - - hasEmailPusher: function(pushers, address) { - return this.getEmailPusher(pushers, address) !== undefined; - }, - - addEmailPusher: function(address, data) { - return MatrixClientPeg.get().setPusher({ - kind: 'email', - app_id: 'm.email', - pushkey: address, - app_display_name: 'Email Notifications', - device_display_name: address, - lang: navigator.language, - data: data, - append: true, // We always append for email pushers since we don't want to stop other accounts notifying to the same email address - }); - }, -}; diff --git a/src/Velociraptor.js b/src/Velociraptor.js index ad51f66ae3..d2cae5c2a7 100644 --- a/src/Velociraptor.js +++ b/src/Velociraptor.js @@ -1,7 +1,7 @@ const React = require('react'); const ReactDom = require('react-dom'); import PropTypes from 'prop-types'; -const Velocity = require('velocity-vector'); +const Velocity = require('velocity-animate'); /** * The Velociraptor contains components and animates transitions with velocity. diff --git a/src/VelocityBounce.js b/src/VelocityBounce.js index b9513249b8..db216f81fb 100644 --- a/src/VelocityBounce.js +++ b/src/VelocityBounce.js @@ -1,4 +1,4 @@ -const Velocity = require('velocity-vector'); +const Velocity = require('velocity-animate'); // courtesy of https://github.com/julianshapiro/velocity/issues/283 // We only use easeOutBounce (easeInBounce is just sort of nonsensical) diff --git a/src/WidgetMessaging.js b/src/WidgetMessaging.js index 5b722df65f..1d8e1b9cd3 100644 --- a/src/WidgetMessaging.js +++ b/src/WidgetMessaging.js @@ -1,5 +1,6 @@ /* Copyright 2017 New Vector Ltd +Copyright 2019 Travis Ralston Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,6 +22,11 @@ limitations under the License. import FromWidgetPostMessageApi from './FromWidgetPostMessageApi'; import ToWidgetPostMessageApi from './ToWidgetPostMessageApi'; +import Modal from "./Modal"; +import MatrixClientPeg from "./MatrixClientPeg"; +import SettingsStore from "./settings/SettingsStore"; +import WidgetOpenIDPermissionsDialog from "./components/views/dialogs/WidgetOpenIDPermissionsDialog"; +import WidgetUtils from "./utils/WidgetUtils"; if (!global.mxFromWidgetMessaging) { global.mxFromWidgetMessaging = new FromWidgetPostMessageApi(); @@ -34,12 +40,14 @@ if (!global.mxToWidgetMessaging) { const OUTBOUND_API_NAME = 'toWidget'; export default class WidgetMessaging { - constructor(widgetId, widgetUrl, target) { + constructor(widgetId, widgetUrl, isUserWidget, target) { this.widgetId = widgetId; this.widgetUrl = widgetUrl; + this.isUserWidget = isUserWidget; this.target = target; this.fromWidget = global.mxFromWidgetMessaging; this.toWidget = global.mxToWidgetMessaging; + this._onOpenIdRequest = this._onOpenIdRequest.bind(this); this.start(); } @@ -109,9 +117,57 @@ export default class WidgetMessaging { start() { this.fromWidget.addEndpoint(this.widgetId, this.widgetUrl); + this.fromWidget.addListener("get_openid", this._onOpenIdRequest); } stop() { this.fromWidget.removeEndpoint(this.widgetId, this.widgetUrl); + this.fromWidget.removeListener("get_openid", this._onOpenIdRequest); + } + + async _onOpenIdRequest(ev, rawEv) { + if (ev.widgetId !== this.widgetId) return; // not interesting + + const widgetSecurityKey = WidgetUtils.getWidgetSecurityKey(this.widgetId, this.widgetUrl, this.isUserWidget); + + const settings = SettingsStore.getValue("widgetOpenIDPermissions"); + if (settings.deny && settings.deny.includes(widgetSecurityKey)) { + this.fromWidget.sendResponse(rawEv, {state: "blocked"}); + return; + } + if (settings.allow && settings.allow.includes(widgetSecurityKey)) { + const responseBody = {state: "allowed"}; + const credentials = await MatrixClientPeg.get().getOpenIdToken(); + Object.assign(responseBody, credentials); + this.fromWidget.sendResponse(rawEv, responseBody); + return; + } + + // Confirm that we received the request + this.fromWidget.sendResponse(rawEv, {state: "request"}); + + // Actually ask for permission to send the user's data + Modal.createTrackedDialog("OpenID widget permissions", '', + WidgetOpenIDPermissionsDialog, { + widgetUrl: this.widgetUrl, + widgetId: this.widgetId, + isUserWidget: this.isUserWidget, + + onFinished: async (confirm) => { + const responseBody = {success: confirm}; + if (confirm) { + const credentials = await MatrixClientPeg.get().getOpenIdToken(); + Object.assign(responseBody, credentials); + } + this.messageToWidget({ + api: OUTBOUND_API_NAME, + action: "openid_credentials", + data: responseBody, + }).catch((error) => { + console.error("Failed to send OpenID credentials: ", error); + }); + }, + }, + ); } } diff --git a/src/actions/MatrixActionCreators.js b/src/actions/MatrixActionCreators.js index c1d42ffd0d..c89ec44435 100644 --- a/src/actions/MatrixActionCreators.js +++ b/src/actions/MatrixActionCreators.js @@ -131,6 +131,24 @@ function createRoomTagsAction(matrixClient, roomTagsEvent, room) { return { action: 'MatrixActions.Room.tags', room }; } +/** + * Create a MatrixActions.Room.receipt action that represents a MatrixClient + * `Room.receipt` event, each parameter mapping to a key-value in the action. + * + * @param {MatrixClient} matrixClient the matrix client + * @param {MatrixEvent} event the receipt event. + * @param {Room} room the room the receipt happened in. + * @returns {Object} an action of type MatrixActions.Room.receipt. + */ +function createRoomReceiptAction(matrixClient, event, room) { + return { + action: 'MatrixActions.Room.receipt', + event, + room, + matrixClient, + }; +} + /** * @typedef RoomTimelineAction * @type {Object} @@ -233,6 +251,7 @@ export default { 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); diff --git a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js index 2c54344a30..9ceff69467 100644 --- a/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js +++ b/src/async-components/views/dialogs/keybackup/CreateKeyBackupDialog.js @@ -240,7 +240,6 @@ export default React.createClass({ _renderPhasePassPhrase: function() { const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let strengthMeter; let helpText; @@ -265,8 +264,15 @@ export default React.createClass({ } return
-

{_t("Secure your encrypted message history with a Recovery Passphrase.")}

-

{_t("You'll need it if you log out or lose access to this device.")}

+

{_t( + "Warning: you should only set up key backup from a trusted computer.", {}, + { b: sub => {sub} }, + )}

+

{_t( + "We'll store an encrypted copy of your keys on our server. " + + "Protect your backup with a passphrase to keep it secure.", + )}

+

{_t("For maximum security, this should be different from your account password.")}

@@ -291,34 +297,12 @@ export default React.createClass({ disabled={!this._passPhraseIsValid()} /> -

{_t( - "If you don't want encrypted message history to be available on other devices, "+ - ".", - {}, - { - button: sub => - {sub} - , - }, - )}

-

{_t( - "Or, if you don't want to create a Recovery Passphrase, skip this step and "+ - ".", - {}, - { - button: sub => - {sub} - , - }, - )}

+
+ {_t("Advanced")} +

+
; }, @@ -353,9 +337,7 @@ export default React.createClass({ const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return

{_t( - "Type in your Recovery Passphrase to confirm you remember it. " + - "If it helps, add it to your password manager or store it " + - "somewhere safe.", + "Please enter your passphrase a second time to confirm.", )}

@@ -392,7 +374,13 @@ export default React.createClass({ } return
-

{_t("Make a copy of this Recovery Key and keep it safe.")}

+

{_t( + "Your recovery key is a safety net - you can use it to restore " + + "access to your encrypted messages if you forget your passphrase.", + )}

+

{_t( + "Keep your recovery key somewhere very secure, like a password manager (or a safe)", + )}

{bodyText}

@@ -455,10 +443,9 @@ export default React.createClass({ const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return

{_t( - "Your encryption keys are now being backed up in the background " + - "to your Homeserver. The initial backup could take several minutes. " + - "You can view key backup upload progress in Settings.")}

- + @@ -484,19 +471,19 @@ export default React.createClass({ _titleForPhase: function(phase) { switch (phase) { case PHASE_PASSPHRASE: - return _t('Create a Recovery Passphrase'); + return _t('Secure your backup with a passphrase'); case PHASE_PASSPHRASE_CONFIRM: - return _t('Confirm Recovery Passphrase'); + return _t('Confirm your passphrase'); case PHASE_OPTOUT_CONFIRM: return _t('Warning!'); case PHASE_SHOWKEY: - return _t('Recovery Key'); + return _t('Recovery key'); case PHASE_KEEPITSAFE: return _t('Keep it safe'); case PHASE_BACKINGUP: return _t('Starting backup...'); case PHASE_DONE: - return _t('Backup Started'); + return _t('Success!'); default: return _t("Create Key Backup"); } diff --git a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js index db86178b5a..28281af771 100644 --- a/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js +++ b/src/async-components/views/dialogs/keybackup/NewRecoveryMethodDialog.js @@ -39,36 +39,8 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { } onSetupClick = async () => { - // TODO: Should change to a restore key backup flow that checks the - // recovery passphrase while at the same time also cross-signing the - // device as well in a single flow. Since we don't have that yet, we'll - // look for an unverified device and verify it. Note that this means - // we won't restore keys yet; instead we'll only trust the backup for - // sending our own new keys to it. - let backupSigStatus; - try { - backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(this.props.newVersionInfo); - } catch (e) { - console.log("Unable to fetch key backup status", e); - return; - } - - let unverifiedDevice; - for (const sig of backupSigStatus.sigs) { - if (!sig.device.isVerified()) { - unverifiedDevice = sig.device; - break; - } - } - if (!unverifiedDevice) { - console.log("Unable to find a device to verify."); - return; - } - - const DeviceVerifyDialog = sdk.getComponent('views.dialogs.DeviceVerifyDialog'); - Modal.createTrackedDialog('Device Verify Dialog', '', DeviceVerifyDialog, { - userId: MatrixClientPeg.get().credentials.userId, - device: unverifiedDevice, + const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog'); + Modal.createTrackedDialog('Restore Backup', '', RestoreKeyBackupDialog, { onFinished: this.props.onFinished, }); } @@ -111,11 +83,6 @@ export default class NewRecoveryMethodDialog extends React.PureComponent { } else { content =
{newMethodDetected} -

{_t( - "Setting up Secure Messages on this device " + - "will re-encrypt this device's message history with " + - "the new recovery method.", - )}

{hackWarning} EmojiData[key]).sort( (a, b) => { if (a.category === b.category) { return a.emoji_order - b.emoji_order; } - return CATEGORY_ORDER.indexOf(a.category) - CATEGORY_ORDER.indexOf(b.category); + return a.category - b.category; }, ).map((a, index) => { return { @@ -101,26 +85,20 @@ export default class EmojiProvider extends AutocompleteProvider { return []; // don't give any suggestions if the user doesn't want them } - const EmojiText = sdk.getComponent('views.elements.EmojiText'); - let completions = []; const {command, range} = this.getCurrentCommand(query, selection); if (command) { - let matchedString = command[0]; - - // Remove prefix of any length (single whitespace or unicode emoji) - const prefixMatch = MATCH_PREFIX_REGEX.exec(matchedString); - if (prefixMatch) { - matchedString = matchedString.slice(prefixMatch[0].length); - range.start += prefixMatch[0].length; - } + const matchedString = command[0]; completions = this.matcher.match(matchedString); // Do second match with shouldMatchWordsOnly in order to match against 'name' completions = completions.concat(this.nameMatcher.match(matchedString)); const sorters = []; - // First, sort by score (Infinity if matchedString not in shortname) + // make sure that emoticons come first + sorters.push((c) => score(matchedString, c.aliases_ascii)); + + // then sort by score (Infinity if matchedString not in shortname) sorters.push((c) => score(matchedString, c.shortname)); // If the matchedString is not empty, sort by length of shortname. Example: // matchedString = ":bookmark" @@ -133,12 +111,12 @@ export default class EmojiProvider extends AutocompleteProvider { completions = _sortBy(_uniq(completions), sorters); completions = completions.map((result) => { - const {shortname} = result; - const unicode = shortnameToUnicode(shortname); + const { shortname } = result; + const unicode = shortcodeToUnicode(shortname); return { completion: unicode, component: ( - { unicode }} /> + { unicode }} /> ), range, }; diff --git a/src/autocomplete/RoomProvider.js b/src/autocomplete/RoomProvider.js index 483506557f..f118a06b9e 100644 --- a/src/autocomplete/RoomProvider.js +++ b/src/autocomplete/RoomProvider.js @@ -56,7 +56,7 @@ export default class RoomProvider extends AutocompleteProvider { const {command, range} = this.getCurrentCommand(query, selection, force); if (command) { // the only reason we need to do this is because Fuse only matches on properties - this.matcher.setObjects(client.getRooms().filter( + let matcherObjects = client.getRooms().filter( (room) => !!room && !!getDisplayAliasForRoom(room), ).map((room) => { return { @@ -64,7 +64,21 @@ export default class RoomProvider extends AutocompleteProvider { name: room.name, displayedAlias: getDisplayAliasForRoom(room), }; - })); + }); + + // Filter out any matches where the user will have also autocompleted new rooms + matcherObjects = matcherObjects.filter((r) => { + const tombstone = r.room.currentState.getStateEvents("m.room.tombstone", ""); + if (tombstone && tombstone.getContent() && tombstone.getContent()['replacement_room']) { + const hasReplacementRoom = matcherObjects.some( + (r2) => r2.room.roomId === tombstone.getContent()['replacement_room'], + ); + return !hasReplacementRoom; + } + return true; + }); + + this.matcher.setObjects(matcherObjects); const matchedString = command[0]; completions = this.matcher.match(matchedString); completions = _sortBy(completions, [ diff --git a/src/components/structures/AutoHideScrollbar.js b/src/components/structures/AutoHideScrollbar.js index a385df0401..3f27f51f18 100644 --- a/src/components/structures/AutoHideScrollbar.js +++ b/src/components/structures/AutoHideScrollbar.js @@ -114,10 +114,17 @@ export default class AutoHideScrollbar extends React.Component { } } + getScrollTop() { + return this.containerRef.scrollTop; + } + render() { return (
{ this.props.children } diff --git a/src/components/structures/BottomLeftMenu.js b/src/components/structures/BottomLeftMenu.js deleted file mode 100644 index 47b30be450..0000000000 --- a/src/components/structures/BottomLeftMenu.js +++ /dev/null @@ -1,197 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import React from 'react'; -import ReactDOM from 'react-dom'; -import sdk from '../../index'; -import dis from '../../dispatcher'; -import Velocity from 'velocity-vector'; -import 'velocity-vector/velocity.ui'; -import SettingsStore from '../../settings/SettingsStore'; - -const CALLOUT_ANIM_DURATION = 1000; - -module.exports = React.createClass({ - displayName: 'BottomLeftMenu', - - propTypes: { - collapsed: React.PropTypes.bool.isRequired, - }, - - getInitialState: function() { - return ({ - directoryHover: false, - roomsHover: false, - homeHover: false, - peopleHover: false, - settingsHover: false, - }); - }, - - componentWillMount: function() { - this._dispatcherRef = dis.register(this.onAction); - this._peopleButton = null; - this._directoryButton = null; - this._createRoomButton = null; - this._lastCallouts = {}; - }, - - componentWillUnmount: function() { - dis.unregister(this._dispatcherRef); - }, - - // Room events - onDirectoryClick: function() { - dis.dispatch({ action: 'view_room_directory' }); - }, - - onDirectoryMouseEnter: function() { - this.setState({ directoryHover: true }); - }, - - onDirectoryMouseLeave: function() { - this.setState({ directoryHover: false }); - }, - - onRoomsClick: function() { - dis.dispatch({ action: 'view_create_room' }); - }, - - onRoomsMouseEnter: function() { - this.setState({ roomsHover: true }); - }, - - onRoomsMouseLeave: function() { - this.setState({ roomsHover: false }); - }, - - // Home button events - onHomeClick: function() { - dis.dispatch({ action: 'view_home_page' }); - }, - - onHomeMouseEnter: function() { - this.setState({ homeHover: true }); - }, - - onHomeMouseLeave: function() { - this.setState({ homeHover: false }); - }, - - // People events - onPeopleClick: function() { - dis.dispatch({ action: 'view_create_chat' }); - }, - - onPeopleMouseEnter: function() { - this.setState({ peopleHover: true }); - }, - - onPeopleMouseLeave: function() { - this.setState({ peopleHover: false }); - }, - - // Settings events - onSettingsClick: function() { - dis.dispatch({ action: 'view_user_settings' }); - }, - - onSettingsMouseEnter: function() { - this.setState({ settingsHover: true }); - }, - - onSettingsMouseLeave: function() { - this.setState({ settingsHover: false }); - }, - - onAction: function(payload) { - let calloutElement; - switch (payload.action) { - // Incoming instruction: dance! - case 'callout_start_chat': - calloutElement = this._peopleButton; - break; - case 'callout_room_directory': - calloutElement = this._directoryButton; - break; - case 'callout_create_room': - calloutElement = this._createRoomButton; - break; - } - if (calloutElement) { - const lastCallout = this._lastCallouts[payload.action]; - const now = Date.now(); - if (lastCallout == undefined || lastCallout < now - CALLOUT_ANIM_DURATION) { - this._lastCallouts[payload.action] = now; - Velocity(ReactDOM.findDOMNode(calloutElement), "callout.bounce", CALLOUT_ANIM_DURATION); - } - } - }, - - // Get the label/tooltip to show - getLabel: function(label, show) { - if (show) { - const RoomTooltip = sdk.getComponent("rooms.RoomTooltip"); - return ; - } - }, - - _collectPeopleButton: function(e) { - this._peopleButton = e; - }, - - _collectDirectoryButton: function(e) { - this._directoryButton = e; - }, - - _collectCreateRoomButton: function(e) { - this._createRoomButton = e; - }, - - render: function() { - const HomeButton = sdk.getComponent('elements.HomeButton'); - const StartChatButton = sdk.getComponent('elements.StartChatButton'); - const RoomDirectoryButton = sdk.getComponent('elements.RoomDirectoryButton'); - const CreateRoomButton = sdk.getComponent('elements.CreateRoomButton'); - const SettingsButton = sdk.getComponent('elements.SettingsButton'); - const GroupsButton = sdk.getComponent('elements.GroupsButton'); - - const groupsButton = !SettingsStore.getValue("TagPanel.enableTagPanel") ? - : null; - - return ( -
-
- -
- -
-
- -
-
- -
- { groupsButton } - - - -
-
- ); - }, -}); diff --git a/src/components/structures/CompatibilityPage.js b/src/components/structures/CompatibilityPage.js index 28521cb1b7..c6abba4eb3 100644 --- a/src/components/structures/CompatibilityPage.js +++ b/src/components/structures/CompatibilityPage.js @@ -48,19 +48,13 @@ module.exports = React.createClass({

{ _t( - 'Please install Chrome or Firefox ' + - 'for the best experience.', + 'Please install Chrome, Firefox, ' + + 'or Safari for the best experience.', {}, { 'chromeLink': (sub) => {sub}, - 'firefoxLink': (sub) => {sub}, - }, - )} - { _t('Safari and Opera work too.', - {}, - { - 'safariLink': (sub) => {sub}, - 'operaLink': (sub) => {sub}, + 'firefoxLink': (sub) => {sub}, + 'safariLink': (sub) => {sub}, }, )}

diff --git a/src/components/structures/ContextualMenu.js b/src/components/structures/ContextualMenu.js index d551a6fe27..345eae2b18 100644 --- a/src/components/structures/ContextualMenu.js +++ b/src/components/structures/ContextualMenu.js @@ -56,6 +56,7 @@ export default class ContextualMenu extends React.Component { menuPaddingRight: PropTypes.number, menuPaddingBottom: PropTypes.number, menuPaddingLeft: PropTypes.number, + zIndex: PropTypes.number, // If true, insert an invisible screen-sized element behind the // menu that when clicked will close it. @@ -215,16 +216,22 @@ export default class ContextualMenu extends React.Component { menuStyle["paddingRight"] = props.menuPaddingRight; } + const wrapperStyle = {}; + if (!isNaN(Number(props.zIndex))) { + menuStyle["zIndex"] = props.zIndex + 1; + wrapperStyle["zIndex"] = props.zIndex; + } + const ElementClass = props.elementClass; // FIXME: If a menu uses getDefaultProps it clobbers the onFinished // property set here so you can't close the menu from a button click! - return
+ return
{ chevron }
- { props.hasBackground &&
}
; diff --git a/src/components/structures/CustomRoomTagPanel.js b/src/components/structures/CustomRoomTagPanel.js new file mode 100644 index 0000000000..ee69d800ed --- /dev/null +++ b/src/components/structures/CustomRoomTagPanel.js @@ -0,0 +1,125 @@ +/* +Copyright 2019 New Vector Ltd. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import CustomRoomTagStore from '../../stores/CustomRoomTagStore'; +import AutoHideScrollbar from './AutoHideScrollbar'; +import sdk from '../../index'; +import dis from '../../dispatcher'; +import classNames from 'classnames'; +import * as FormattingUtils from '../../utils/FormattingUtils'; + +class CustomRoomTagPanel extends React.Component { + constructor(props) { + super(props); + this.state = { + tags: CustomRoomTagStore.getSortedTags(), + }; + } + + componentWillMount() { + this._tagStoreToken = CustomRoomTagStore.addListener(() => { + this.setState({tags: CustomRoomTagStore.getSortedTags()}); + }); + } + + componentWillUnmount() { + if (this._tagStoreToken) { + this._tagStoreToken.remove(); + } + } + + render() { + const tags = this.state.tags.map((tag) => { + return (); + }); + + const classes = classNames('mx_CustomRoomTagPanel', { + mx_CustomRoomTagPanel_empty: this.state.tags.length === 0, + }); + + return (
+
+ + {tags} + +
); + } +} + +class CustomRoomTagTile extends React.Component { + constructor(props) { + super(props); + this.state = {hover: false}; + this.onClick = this.onClick.bind(this); + this.onMouseOut = this.onMouseOut.bind(this); + this.onMouseOver = this.onMouseOver.bind(this); + } + + onMouseOver() { + this.setState({hover: true}); + } + + onMouseOut() { + this.setState({hover: false}); + } + + onClick() { + dis.dispatch({action: 'select_custom_room_tag', tag: this.props.tag.name}); + } + + render() { + const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); + const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + const Tooltip = sdk.getComponent('elements.Tooltip'); + + const tag = this.props.tag; + const avatarHeight = 40; + const className = classNames({ + CustomRoomTagPanel_tileSelected: tag.selected, + }); + const name = tag.name; + const badge = tag.badge; + let badgeElement; + if (badge) { + const badgeClasses = classNames({ + "mx_TagTile_badge": true, + "mx_TagTile_badgeHighlight": badge.highlight, + }); + badgeElement = (
{FormattingUtils.formatCount(badge.count)}
); + } + + const tip = (this.state.hover ? + : +
); + return ( + +
+ + { badgeElement } + { tip } +
+
+ ); + } +} + +export default CustomRoomTagPanel; diff --git a/src/components/structures/HomePage.js b/src/components/structures/EmbeddedPage.js similarity index 59% rename from src/components/structures/HomePage.js rename to src/components/structures/EmbeddedPage.js index 18f1a2ba31..d10b7f8414 100644 --- a/src/components/structures/HomePage.js +++ b/src/components/structures/EmbeddedPage.js @@ -1,6 +1,7 @@ /* Copyright 2016 OpenMarket Ltd Copyright 2017 Vector Creations Ltd +Copyright 2019 New Vector Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -26,22 +27,27 @@ import sdk from '../../index'; import { MatrixClient } from 'matrix-js-sdk'; import classnames from 'classnames'; -class HomePage extends React.Component { - static displayName = 'HomePage'; - +export default class EmbeddedPage extends React.PureComponent { static propTypes = { - // URL to use as the iFrame src. Defaults to /home.html. - homePageUrl: PropTypes.string, + // URL to request embedded page content from + url: PropTypes.string, + // Class name prefix to apply for a given instance + className: PropTypes.string, + // Whether to wrap the page in a scrollbar + scrollbar: PropTypes.bool, }; static contextTypes = { matrixClient: PropTypes.instanceOf(MatrixClient), }; - state = { - iframeSrc: '', + constructor(props) { + super(props); + + this.state = { page: '', - }; + }; + } translate(s) { // default implementation - skins may wish to extend this @@ -51,22 +57,24 @@ class HomePage extends React.Component { componentWillMount() { this._unmounted = false; - // we use request() to inline the homepage into the react component + if (!this.props.url) { + return; + } + + // we use request() to inline the page into the react component // so that it can inherit CSS and theming easily rather than mess around // with iframes and trying to synchronise document.stylesheets. - const src = this.props.homePageUrl || 'home.html'; - request( - { method: "GET", url: src }, + { method: "GET", url: this.props.url }, (err, response, body) => { if (this._unmounted) { return; } if (err || response.status < 200 || response.status >= 300) { - console.warn(`Error loading home page: ${err}`); - this.setState({ page: _t("Couldn't load home page") }); + console.warn(`Error loading page: ${err}`); + this.setState({ page: _t("Couldn't load page") }); return; } @@ -81,28 +89,28 @@ class HomePage extends React.Component { } render() { - const isGuest = this.context.matrixClient.isGuest(); + const client = this.context.matrixClient; + const isGuest = client ? client.isGuest() : true; + const className = this.props.className; const classes = classnames({ - mx_HomePage: true, - mx_HomePage_guest: isGuest, + [className]: true, + [`${className}_guest`]: isGuest, }); - if (this.state.iframeSrc) { - return ( -
-