diff --git a/res/css/_common.scss b/res/css/_common.scss
index cf48358a4e..c087df04cb 100644
--- a/res/css/_common.scss
+++ b/res/css/_common.scss
@@ -428,6 +428,10 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus {
border-radius: 8px;
padding: 0px;
box-shadow: none;
+
+ /* Don't show scroll-bars on spinner dialogs */
+ overflow-x: hidden;
+ overflow-y: hidden;
}
// TODO: Review mx_GeneralButton usage to see if it can use a different class
diff --git a/res/css/views/elements/_InlineSpinner.scss b/res/css/views/elements/_InlineSpinner.scss
index 612b6209c6..6b91e45923 100644
--- a/res/css/views/elements/_InlineSpinner.scss
+++ b/res/css/views/elements/_InlineSpinner.scss
@@ -18,7 +18,7 @@ limitations under the License.
display: inline;
}
-.mx_InlineSpinner img {
+.mx_InlineSpinner_spin img {
margin: 0px 6px;
vertical-align: -3px;
}
diff --git a/res/css/views/elements/_Spinner.scss b/res/css/views/elements/_Spinner.scss
index 01b4f23c2c..6966a60e52 100644
--- a/res/css/views/elements/_Spinner.scss
+++ b/res/css/views/elements/_Spinner.scss
@@ -23,6 +23,16 @@ limitations under the License.
flex: 1;
}
+.mx_Spinner_spin img {
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
.mx_MatrixChat_middlePanel .mx_Spinner {
height: auto;
}
diff --git a/res/img/spinner.svg b/res/img/spinner.svg
new file mode 100644
index 0000000000..a18140c7e2
--- /dev/null
+++ b/res/img/spinner.svg
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/src/components/views/elements/AppTile.js b/src/components/views/elements/AppTile.js
index 9129b8fe48..60cd1a2eba 100644
--- a/src/components/views/elements/AppTile.js
+++ b/src/components/views/elements/AppTile.js
@@ -29,7 +29,7 @@ import { _t } from '../../../languageHandler';
import * as sdk from '../../../index';
import AppPermission from './AppPermission';
import AppWarning from './AppWarning';
-import MessageSpinner from './MessageSpinner';
+import Spinner from './Spinner';
import WidgetUtils from '../../../utils/WidgetUtils';
import dis from '../../../dispatcher/dispatcher';
import ActiveWidgetStore from '../../../stores/ActiveWidgetStore';
@@ -740,7 +740,7 @@ export default class AppTile extends React.Component {
if (this.props.show) {
const loadingElement = (
-
+
);
if (!this.state.hasPermissionToLoad) {
diff --git a/src/components/views/elements/InlineSpinner.js b/src/components/views/elements/InlineSpinner.js
index ad70471d89..ad88868790 100644
--- a/src/components/views/elements/InlineSpinner.js
+++ b/src/components/views/elements/InlineSpinner.js
@@ -16,6 +16,8 @@ limitations under the License.
import React from "react";
import createReactClass from 'create-react-class';
+import {_t} from "../../../languageHandler";
+import SettingsStore from "../../../settings/SettingsStore";
export default createReactClass({
displayName: 'InlineSpinner',
@@ -25,9 +27,25 @@ export default createReactClass({
const h = this.props.h || 16;
const imgClass = this.props.imgClassName || "";
+ let divClass;
+ let imageSource;
+ if (SettingsStore.isFeatureEnabled('feature_new_spinner')) {
+ divClass = "mx_InlineSpinner mx_Spinner_spin";
+ imageSource = require("../../../../res/img/spinner.svg");
+ } else {
+ divClass = "mx_InlineSpinner";
+ imageSource = require("../../../../res/img/spinner.gif");
+ }
+
return (
-
-
+
+
);
},
diff --git a/src/components/views/elements/MessageSpinner.js b/src/components/views/elements/MessageSpinner.js
deleted file mode 100644
index 1775fdd4d7..0000000000
--- a/src/components/views/elements/MessageSpinner.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-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 createReactClass from 'create-react-class';
-
-export default createReactClass({
- displayName: 'MessageSpinner',
-
- render: function() {
- const w = this.props.w || 32;
- const h = this.props.h || 32;
- const imgClass = this.props.imgClassName || "";
- const msg = this.props.msg || "Loading...";
- return (
-
-
{ msg }
-
-
- );
- },
-});
diff --git a/src/components/views/elements/Spinner.js b/src/components/views/elements/Spinner.js
index b1fe97d5d2..08ba0cf921 100644
--- a/src/components/views/elements/Spinner.js
+++ b/src/components/views/elements/Spinner.js
@@ -16,19 +16,39 @@ limitations under the License.
*/
import React from "react";
-import createReactClass from 'create-react-class';
+import PropTypes from "prop-types";
+import {_t} from "../../../languageHandler";
+import SettingsStore from "../../../settings/SettingsStore";
-export default createReactClass({
- displayName: 'Spinner',
+const Spinner = ({w = 32, h = 32, imgClassName, message}) => {
+ let divClass;
+ let imageSource;
+ if (SettingsStore.isFeatureEnabled('feature_new_spinner')) {
+ divClass = "mx_Spinner mx_Spinner_spin";
+ imageSource = require("../../../../res/img/spinner.svg");
+ } else {
+ divClass = "mx_Spinner";
+ imageSource = require("../../../../res/img/spinner.gif");
+ }
- render: function() {
- const w = this.props.w || 32;
- const h = this.props.h || 32;
- const imgClass = this.props.imgClassName || "";
- return (
-
-
-
- );
- },
-});
+ return (
+
+ { message &&
{ message}
}
+
+
+ );
+};
+Spinner.propTypes = {
+ w: PropTypes.number,
+ h: PropTypes.number,
+ imgClassName: PropTypes.string,
+ message: PropTypes.node,
+};
+
+export default Spinner;
diff --git a/src/components/views/messages/MAudioBody.js b/src/components/views/messages/MAudioBody.js
index a642936fec..37f85a108f 100644
--- a/src/components/views/messages/MAudioBody.js
+++ b/src/components/views/messages/MAudioBody.js
@@ -22,6 +22,7 @@ import MFileBody from './MFileBody';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
+import InlineSpinner from '../elements/InlineSpinner';
export default class MAudioBody extends React.Component {
constructor(props) {
@@ -94,7 +95,7 @@ export default class MAudioBody extends React.Component {
// Not sure how tall the audio player is so not sure how tall it should actually be.
return (
-
+
);
}
diff --git a/src/components/views/messages/MImageBody.js b/src/components/views/messages/MImageBody.js
index ad238a728e..c92ae475bf 100644
--- a/src/components/views/messages/MImageBody.js
+++ b/src/components/views/messages/MImageBody.js
@@ -26,6 +26,7 @@ import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
+import InlineSpinner from '../elements/InlineSpinner';
export default class MImageBody extends React.Component {
static propTypes = {
@@ -365,12 +366,7 @@ export default class MImageBody extends React.Component {
// e2e image hasn't been decrypted yet
if (content.file !== undefined && this.state.decryptedUrl === null) {
- placeholder = ;
+ placeholder = ;
} else if (!this.state.imgLoaded) {
// Deliberately, getSpinner is left unimplemented here, MStickerBody overides
placeholder = this.getPlaceholder();
diff --git a/src/components/views/messages/MVideoBody.js b/src/components/views/messages/MVideoBody.js
index 03f345e042..fdc04deffc 100644
--- a/src/components/views/messages/MVideoBody.js
+++ b/src/components/views/messages/MVideoBody.js
@@ -23,6 +23,7 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { decryptFile } from '../../../utils/DecryptFile';
import { _t } from '../../../languageHandler';
import SettingsStore from "../../../settings/SettingsStore";
+import InlineSpinner from '../elements/InlineSpinner';
export default createReactClass({
displayName: 'MVideoBody',
@@ -147,7 +148,7 @@ export default createReactClass({
return (
-
+
);
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 4970a650db..ae44d59c59 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -427,6 +427,7 @@
"Sorry, your homeserver is too old to participate in this room.": "Sorry, your homeserver is too old to participate in this room.",
"Please contact your homeserver administrator.": "Please contact your homeserver administrator.",
"Failed to join room": "Failed to join room",
+ "New spinner design": "New spinner design",
"Font scaling": "Font scaling",
"Message Pinning": "Message Pinning",
"Custom user status messages": "Custom user status messages",
diff --git a/src/settings/Settings.js b/src/settings/Settings.js
index f19b827307..820329f6c6 100644
--- a/src/settings/Settings.js
+++ b/src/settings/Settings.js
@@ -97,6 +97,12 @@ export const SETTINGS = {
// // not use this for new settings.
// invertedSettingName: "my-negative-setting",
// },
+ "feature_new_spinner": {
+ isFeature: true,
+ displayName: _td("New spinner design"),
+ supportedLevels: LEVELS_FEATURE,
+ default: false,
+ },
"feature_font_scaling": {
isFeature: true,
displayName: _td("Font scaling"),