diff --git a/src/GroupAddressPicker.js b/src/GroupAddressPicker.js index cfd2590780..ca495de16d 100644 --- a/src/GroupAddressPicker.js +++ b/src/GroupAddressPicker.js @@ -76,6 +76,13 @@ function _onGroupInviteFinished(groupId, addrs) { title: _t("Failed to invite the following users to %(groupId)s:", {groupId: groupId}), description: errorList.join(", "), }); + } else { + const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); + Modal.createTrackedDialog('Group invitations sent', '', QuestionDialog, { + title: _t("Invites sent"), + description: _t("Your group invitations have been sent."), + hasCancelButton: false, + }); } }).catch((err) => { const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); diff --git a/src/HtmlUtils.js b/src/HtmlUtils.js index 5620bc06df..b8a1c63dfb 100644 --- a/src/HtmlUtils.js +++ b/src/HtmlUtils.js @@ -385,10 +385,9 @@ class TextHighlighter extends BaseHighlighter { * highlights: optional list of words to highlight, ordered by longest word first * * opts.highlightLink: optional href to add to highlighted words + * opts.disableBigEmoji: optional argument to disable the big emoji class. */ -export function bodyToHtml(content, highlights, opts) { - opts = opts || {}; - +export function bodyToHtml(content, highlights, opts={}) { const isHtml = (content.format === "org.matrix.custom.html"); const body = isHtml ? content.formatted_body : escape(content.body); @@ -418,7 +417,7 @@ export function bodyToHtml(content, highlights, opts) { } let emojiBody = false; - if (bodyHasEmoji) { + if (!opts.disableBigEmoji && bodyHasEmoji) { EMOJI_REGEX.lastIndex = 0; const contentBodyTrimmed = content.body !== undefined ? content.body.trim() : ''; const match = EMOJI_REGEX.exec(contentBodyTrimmed); diff --git a/src/UserSettingsStore.js b/src/UserSettingsStore.js index 9b7554bda2..6f689735a7 100644 --- a/src/UserSettingsStore.js +++ b/src/UserSettingsStore.js @@ -1,5 +1,6 @@ /* 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. @@ -17,33 +18,51 @@ limitations under the License. import Promise from 'bluebird'; import MatrixClientPeg from './MatrixClientPeg'; import Notifier from './Notifier'; -import { _t } from './languageHandler'; +import { _t, _td } from './languageHandler'; +import SdkConfig from './SdkConfig'; /* * TODO: Make things use this. This is all WIP - see UserSettings.js for usage. */ +const FEATURES = [ + { + id: 'feature_groups', + name: _td("Groups"), + }, +]; + export default { - LABS_FEATURES: [ - { - name: "-", - id: 'matrix_apps', - default: true, + getLabsFeatures() { + const featuresConfig = SdkConfig.get()['features'] || {}; - // XXX: Always use default, ignore localStorage and remove from labs - override: true, - }, - { - name: "-", - id: 'feature_groups', - default: false, - }, - ], + // The old flag: honourned for backwards compat + const enableLabs = SdkConfig.get()['enableLabs']; - // horrible but it works. The locality makes this somewhat more palatable. - doTranslations: function() { - this.LABS_FEATURES[0].name = _t("Matrix Apps"); - this.LABS_FEATURES[1].name = _t("Groups"); + let labsFeatures; + if (enableLabs) { + labsFeatures = FEATURES; + } else { + labsFeatures = FEATURES.filter((f) => { + const sdkConfigValue = featuresConfig[f.id]; + if (sdkConfigValue === 'labs') { + return true; + } + }); + } + return labsFeatures.map((f) => { + return f.id; + }); + }, + + translatedNameForFeature(featureId) { + const feature = FEATURES.filter((f) => { + return f.id === featureId; + })[0]; + + if (feature === undefined) return null; + + return _t(feature.name); }, loadProfileInfo: function() { @@ -180,33 +199,33 @@ export default { localStorage.setItem('mx_local_settings', JSON.stringify(settings)); }, - getFeatureById(feature: string) { - for (let i = 0; i < this.LABS_FEATURES.length; i++) { - const f = this.LABS_FEATURES[i]; - if (f.id === feature) { - return f; - } - } - return null; - }, - isFeatureEnabled: function(featureId: string): boolean { - // Disable labs for guests. - if (MatrixClientPeg.get().isGuest()) return false; + const featuresConfig = SdkConfig.get()['features']; - const feature = this.getFeatureById(featureId); - if (!feature) { - console.warn(`Unknown feature "${featureId}"`); + // The old flag: honourned for backwards compat + const enableLabs = SdkConfig.get()['enableLabs']; + + let sdkConfigValue = enableLabs ? 'labs' : 'disable'; + if (featuresConfig && featuresConfig[featureId] !== undefined) { + sdkConfigValue = featuresConfig[featureId]; + } + + if (sdkConfigValue === 'enable') { + return true; + } else if (sdkConfigValue === 'disable') { + return false; + } else if (sdkConfigValue === 'labs') { + if (!MatrixClientPeg.get().isGuest()) { + // Make it explicit that guests get the defaults (although they shouldn't + // have been able to ever toggle the flags anyway) + const userValue = localStorage.getItem(`mx_labs_feature_${featureId}`); + return userValue === 'true'; + } + return false; + } else { + console.warn(`Unknown features config for ${featureId}: ${sdkConfigValue}`); return false; } - // Return the default if this feature has an override to be the default value or - // if the feature has never been toggled and is therefore not in localStorage - if (Object.keys(feature).includes('override') || - localStorage.getItem(`mx_labs_feature_${featureId}`) === null - ) { - return feature.default; - } - return localStorage.getItem(`mx_labs_feature_${featureId}`) === 'true'; }, setFeatureEnabled: function(featureId: string, enabled: boolean) { diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 49522747cd..7e6fc05599 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -1,6 +1,7 @@ /* Copyright 2015, 2016 OpenMarket Ltd Copyright 2017 Vector Creations 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. @@ -250,7 +251,6 @@ export default React.createClass({ page_element = ; diff --git a/src/components/structures/RoomView.js b/src/components/structures/RoomView.js index c75b0f61b0..db40380636 100644 --- a/src/components/structures/RoomView.js +++ b/src/components/structures/RoomView.js @@ -118,6 +118,7 @@ module.exports = React.createClass({ canPeek: false, showApps: false, isAlone: false, + isPeeking: false, // error object, as from the matrix client/server API // If we failed to load information about the room, @@ -267,6 +268,7 @@ module.exports = React.createClass({ console.log("Attempting to peek into room %s", roomId); this.setState({ peekLoading: true, + isPeeking: true, // this will change to false if peeking fails }); MatrixClientPeg.get().peekInRoom(roomId).then((room) => { this.setState({ @@ -275,6 +277,11 @@ module.exports = React.createClass({ }); this._onRoomLoaded(room); }, (err) => { + // Stop peeking if anything went wrong + this.setState({ + isPeeking: false, + }); + // This won't necessarily be a MatrixError, but we duck-type // here and say if it's got an 'errcode' key with the right value, // it means we can't peek. @@ -291,6 +298,7 @@ module.exports = React.createClass({ } else if (room) { // Stop peeking because we have joined this room previously MatrixClientPeg.get().stopPeeking(); + this.setState({isPeeking: false}); } }, @@ -1764,8 +1772,8 @@ module.exports = React.createClass({