Merge pull request #4148 from matrix-org/travis/poc/theme-command

Proof of concept for custom theme adding
pull/21833/head
Travis Ralston 2020-03-13 09:26:07 -06:00 committed by GitHub
commit f5f433b347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 97 additions and 3 deletions

View File

@ -42,10 +42,15 @@ pre, code {
font-size: 100% !important; font-size: 100% !important;
} }
.error, .warning { .error, .warning,
.text-error, .text-warning {
color: $warning-color; color: $warning-color;
} }
.text-success {
color: $accent-color;
}
b { b {
// On Firefox, the default weight for `<b>` is `bolder` which results in no bold // On Firefox, the default weight for `<b>` is `bolder` which results in no bold
// effect since we only have specific weights of our fonts available. // effect since we only have specific weights of our fonts available.

View File

@ -61,6 +61,8 @@ export default class GeneralUserSettingsTab extends React.Component {
emails: [], emails: [],
msisdns: [], msisdns: [],
...this._calculateThemeState(), ...this._calculateThemeState(),
customThemeUrl: "",
customThemeMessage: {isError: false, text: ""},
}; };
this.dispatcherRef = dis.register(this._onAction); this.dispatcherRef = dis.register(this._onAction);
@ -274,6 +276,41 @@ export default class GeneralUserSettingsTab extends React.Component {
}); });
}; };
_onAddCustomTheme = async () => {
let currentThemes = SettingsStore.getValue("custom_themes");
if (!currentThemes) currentThemes = [];
currentThemes = currentThemes.map(c => c); // cheap clone
if (this._themeTimer) {
clearTimeout(this._themeTimer);
}
try {
const r = await fetch(this.state.customThemeUrl);
const themeInfo = await r.json();
if (!themeInfo || typeof(themeInfo['name']) !== 'string' || typeof(themeInfo['colors']) !== 'object') {
this.setState({customThemeMessage: {text: _t("Invalid theme schema."), isError: true}});
return;
}
currentThemes.push(themeInfo);
} catch (e) {
console.error(e);
this.setState({customThemeMessage: {text: _t("Error downloading theme information."), isError: true}});
return; // Don't continue on error
}
await SettingsStore.setValue("custom_themes", null, SettingLevel.ACCOUNT, currentThemes);
this.setState({customThemeUrl: "", customThemeMessage: {text: _t("Theme added!"), isError: false}});
this._themeTimer = setTimeout(() => {
this.setState({customThemeMessage: {text: "", isError: false}});
}, 3000);
};
_onCustomThemeChange = (e) => {
this.setState({customThemeUrl: e.target.value});
};
_renderProfileSection() { _renderProfileSection() {
return ( return (
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
@ -368,6 +405,45 @@ export default class GeneralUserSettingsTab extends React.Component {
/> />
</div>; </div>;
} }
let customThemeForm;
if (SettingsStore.isFeatureEnabled("feature_custom_themes")) {
let messageElement = null;
if (this.state.customThemeMessage.text) {
if (this.state.customThemeMessage.isError) {
messageElement = <div className='text-error'>{this.state.customThemeMessage.text}</div>;
} else {
messageElement = <div className='text-success'>{this.state.customThemeMessage.text}</div>;
}
}
customThemeForm = (
<div className='mx_SettingsTab_section'>
<form onSubmit={this._onAddCustomTheme}>
<Field
label={_t("Custom theme URL")}
type='text'
id='mx_GeneralUserSettingsTab_customThemeInput'
autoComplete="off"
onChange={this._onCustomThemeChange}
value={this.state.customThemeUrl}
/>
<AccessibleButton
onClick={this._onAddCustomTheme}
type="submit" kind="primary_sm"
disabled={!this.state.customThemeUrl.trim()}
>{_t("Add theme")}</AccessibleButton>
{messageElement}
</form>
</div>
);
}
const themes = Object.entries(enumerateThemes())
.map(p => ({id: p[0], name: p[1]})); // convert pairs to objects for code readability
const builtInThemes = themes.filter(p => !p.id.startsWith("custom-"));
const customThemes = themes.filter(p => !builtInThemes.includes(p))
.sort((a, b) => a.name.localeCompare(b.name));
const orderedThemes = [...builtInThemes, ...customThemes];
return ( return (
<div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection"> <div className="mx_SettingsTab_section mx_GeneralUserSettingsTab_themeSection">
<span className="mx_SettingsTab_subheading">{_t("Theme")}</span> <span className="mx_SettingsTab_subheading">{_t("Theme")}</span>
@ -376,10 +452,11 @@ export default class GeneralUserSettingsTab extends React.Component {
value={this.state.theme} onChange={this._onThemeChange} value={this.state.theme} onChange={this._onThemeChange}
disabled={this.state.useSystemTheme} disabled={this.state.useSystemTheme}
> >
{Object.entries(enumerateThemes()).map(([theme, text]) => { {orderedThemes.map(theme => {
return <option key={theme} value={theme}>{text}</option>; return <option key={theme.id} value={theme.id}>{theme.name}</option>;
})} })}
</Field> </Field>
{customThemeForm}
<SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} /> <SettingsFlag name="useCompactLayout" level={SettingLevel.ACCOUNT} />
</div> </div>
); );

View File

@ -385,6 +385,7 @@
"Multiple integration managers": "Multiple integration managers", "Multiple integration managers": "Multiple integration managers",
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
"Show a presence dot next to DMs in the room list": "Show a presence dot next to DMs in the room list", "Show a presence dot next to DMs in the room list": "Show a presence dot next to DMs in the room list",
"Support adding custom themes": "Support adding custom themes",
"Enable cross-signing to verify per-user instead of per-session (in development)": "Enable cross-signing to verify per-user instead of per-session (in development)", "Enable cross-signing to verify per-user instead of per-session (in development)": "Enable cross-signing to verify per-user instead of per-session (in development)",
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)", "Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
"Show info about bridges in room settings": "Show info about bridges in room settings", "Show info about bridges in room settings": "Show info about bridges in room settings",
@ -708,12 +709,17 @@
"Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?", "Failed to change password. Is your password correct?": "Failed to change password. Is your password correct?",
"Success": "Success", "Success": "Success",
"Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them", "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them": "Your password was successfully changed. You will not receive push notifications on other sessions until you log back in to them",
"Invalid theme schema.": "Invalid theme schema.",
"Error downloading theme information.": "Error downloading theme information.",
"Theme added!": "Theme added!",
"Profile": "Profile", "Profile": "Profile",
"Email addresses": "Email addresses", "Email addresses": "Email addresses",
"Phone numbers": "Phone numbers", "Phone numbers": "Phone numbers",
"Set a new account password...": "Set a new account password...", "Set a new account password...": "Set a new account password...",
"Account": "Account", "Account": "Account",
"Language and region": "Language and region", "Language and region": "Language and region",
"Custom theme URL": "Custom theme URL",
"Add theme": "Add theme",
"Theme": "Theme", "Theme": "Theme",
"Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.", "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.": "Agree to the identity server (%(serverName)s) Terms of Service to allow yourself to be discoverable by email address or phone number.",
"Account management": "Account management", "Account management": "Account management",

View File

@ -134,6 +134,12 @@ export const SETTINGS = {
supportedLevels: LEVELS_FEATURE, supportedLevels: LEVELS_FEATURE,
default: false, default: false,
}, },
"feature_custom_themes": {
isFeature: true,
displayName: _td("Support adding custom themes"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"mjolnirRooms": { "mjolnirRooms": {
supportedLevels: ['account'], supportedLevels: ['account'],
default: [], default: [],