mirror of https://github.com/vector-im/riot-web
Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/15178
Conflicts: src/settings/Settings.ts src/settings/UIFeature.tspull/21833/head
commit
5bf6697e48
|
@ -170,15 +170,19 @@ class Analytics {
|
|||
return !this.baseUrl;
|
||||
}
|
||||
|
||||
canEnable() {
|
||||
const config = SdkConfig.get();
|
||||
return navigator.doNotTrack !== "1" && config && config.piwik && config.piwik.url && config.piwik.siteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable Analytics if initialized but disabled
|
||||
* otherwise try and initalize, no-op if piwik config missing
|
||||
*/
|
||||
async enable() {
|
||||
if (!this.disabled) return;
|
||||
|
||||
if (!this.canEnable()) return;
|
||||
const config = SdkConfig.get();
|
||||
if (!config || !config.piwik || !config.piwik.url || !config.piwik.siteId) return;
|
||||
|
||||
this.baseUrl = new URL("piwik.php", config.piwik.url);
|
||||
// set constants
|
||||
|
|
|
@ -44,6 +44,8 @@ import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
|
|||
import { Action } from "./dispatcher/actions";
|
||||
import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership";
|
||||
import SdkConfig from "./SdkConfig";
|
||||
import SettingsStore from "./settings/SettingsStore";
|
||||
import {UIFeature} from "./settings/UIFeature";
|
||||
|
||||
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
||||
interface HTMLInputEvent extends Event {
|
||||
|
@ -797,6 +799,7 @@ export const Commands = [
|
|||
command: 'addwidget',
|
||||
args: '<url | embed code | Jitsi url>',
|
||||
description: _td('Adds a custom widget by URL to the room'),
|
||||
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets),
|
||||
runFn: function(roomId, widgetUrl) {
|
||||
if (!widgetUrl) {
|
||||
return reject(_t("Please supply a widget URL or embed code"));
|
||||
|
|
|
@ -79,6 +79,7 @@ import { SettingLevel } from "../../settings/SettingLevel";
|
|||
import { leaveRoomBehaviour } from "../../utils/membership";
|
||||
import CreateCommunityPrototypeDialog from "../views/dialogs/CreateCommunityPrototypeDialog";
|
||||
import ThreepidInviteStore, { IThreepidInvite, IThreepidInviteWireFormat } from "../../stores/ThreepidInviteStore";
|
||||
import {UIFeature} from "../../settings/UIFeature";
|
||||
|
||||
/** constants for MatrixChat.state.view */
|
||||
export enum Views {
|
||||
|
@ -1233,8 +1234,8 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
|
||||
StorageManager.tryPersistStorage();
|
||||
|
||||
if (SettingsStore.getValue("showCookieBar") && this.props.config.piwik && navigator.doNotTrack !== "1") {
|
||||
showAnalyticsToast(this.props.config.piwik && this.props.config.piwik.policyUrl);
|
||||
if (SettingsStore.getValue("showCookieBar") && Analytics.canEnable()) {
|
||||
showAnalyticsToast(this.props.config.piwik?.policyUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1372,15 +1373,19 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
ready: true,
|
||||
});
|
||||
});
|
||||
cli.on('Call.incoming', function(call) {
|
||||
// we dispatch this synchronously to make sure that the event
|
||||
// handlers on the call are set up immediately (so that if
|
||||
// we get an immediate hangup, we don't get a stuck call)
|
||||
dis.dispatch({
|
||||
action: 'incoming_call',
|
||||
call: call,
|
||||
}, true);
|
||||
});
|
||||
|
||||
if (SettingsStore.getValue(UIFeature.Voip)) {
|
||||
cli.on('Call.incoming', function(call) {
|
||||
// we dispatch this synchronously to make sure that the event
|
||||
// handlers on the call are set up immediately (so that if
|
||||
// we get an immediate hangup, we don't get a stuck call)
|
||||
dis.dispatch({
|
||||
action: 'incoming_call',
|
||||
call: call,
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
|
||||
cli.on('Session.logged_out', function(errObj) {
|
||||
if (Lifecycle.isLoggingOut()) return;
|
||||
|
||||
|
|
|
@ -70,10 +70,10 @@ export default class RoomDirectory extends React.Component {
|
|||
this.scrollPanel = null;
|
||||
this.protocols = null;
|
||||
|
||||
this.setState({protocolsLoading: true});
|
||||
this.state.protocolsLoading = true;
|
||||
if (!MatrixClientPeg.get()) {
|
||||
// We may not have a client yet when invoked from welcome page
|
||||
this.setState({protocolsLoading: false});
|
||||
this.state.protocolsLoading = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -102,14 +102,16 @@ export default class RoomDirectory extends React.Component {
|
|||
});
|
||||
} else {
|
||||
// We don't use the protocols in the communities v2 prototype experience
|
||||
this.setState({protocolsLoading: false});
|
||||
this.state.protocolsLoading = false;
|
||||
|
||||
// Grab the profile info async
|
||||
FlairStore.getGroupProfileCached(MatrixClientPeg.get(), this.state.selectedCommunityId).then(profile => {
|
||||
this.setState({communityName: profile.name});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refreshRoomList();
|
||||
}
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ import dis from "../../dispatcher/dispatcher";
|
|||
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
|
||||
import ErrorDialog from "../views/dialogs/ErrorDialog";
|
||||
import EditCommunityPrototypeDialog from "../views/dialogs/EditCommunityPrototypeDialog";
|
||||
import {UIFeature} from "../../settings/UIFeature";
|
||||
|
||||
interface IProps {
|
||||
isMinimized: boolean;
|
||||
|
@ -285,6 +286,15 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
);
|
||||
}
|
||||
|
||||
let feedbackButton;
|
||||
if (SettingsStore.getValue(UIFeature.Feedback)) {
|
||||
feedbackButton = <IconizedContextMenuOption
|
||||
iconClassName="mx_UserMenu_iconMessage"
|
||||
label={_t("Feedback")}
|
||||
onClick={this.onProvideFeedback}
|
||||
/>;
|
||||
}
|
||||
|
||||
let primaryHeader = (
|
||||
<div className="mx_UserMenu_contextMenu_name">
|
||||
<span className="mx_UserMenu_contextMenu_displayName">
|
||||
|
@ -319,11 +329,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
label={_t("Archived rooms")}
|
||||
onClick={this.onShowArchived}
|
||||
/> */}
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_UserMenu_iconMessage"
|
||||
label={_t("Feedback")}
|
||||
onClick={this.onProvideFeedback}
|
||||
/>
|
||||
{ feedbackButton }
|
||||
</IconizedContextMenuOptionList>
|
||||
<IconizedContextMenuOptionList red>
|
||||
<IconizedContextMenuOption
|
||||
|
@ -384,11 +390,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
|||
aria-label={_t("User settings")}
|
||||
onClick={(e) => this.onSettingsOpen(e, null)}
|
||||
/>
|
||||
<IconizedContextMenuOption
|
||||
iconClassName="mx_UserMenu_iconMessage"
|
||||
label={_t("Feedback")}
|
||||
onClick={this.onProvideFeedback}
|
||||
/>
|
||||
{ feedbackButton }
|
||||
</IconizedContextMenuOptionList>
|
||||
<IconizedContextMenuOptionList red>
|
||||
<IconizedContextMenuOption
|
||||
|
|
|
@ -107,12 +107,16 @@ export default class UserSettingsDialog extends React.Component {
|
|||
"mx_UserSettingsDialog_preferencesIcon",
|
||||
<PreferencesUserSettingsTab />,
|
||||
));
|
||||
tabs.push(new Tab(
|
||||
USER_VOICE_TAB,
|
||||
_td("Voice & Video"),
|
||||
"mx_UserSettingsDialog_voiceIcon",
|
||||
<VoiceUserSettingsTab />,
|
||||
));
|
||||
|
||||
if (SettingsStore.getValue(UIFeature.Voip)) {
|
||||
tabs.push(new Tab(
|
||||
USER_VOICE_TAB,
|
||||
_td("Voice & Video"),
|
||||
"mx_UserSettingsDialog_voiceIcon",
|
||||
<VoiceUserSettingsTab />,
|
||||
));
|
||||
}
|
||||
|
||||
tabs.push(new Tab(
|
||||
USER_SECURITY_TAB,
|
||||
_td("Security & Privacy"),
|
||||
|
|
|
@ -20,6 +20,7 @@ import { _t } from '../../../languageHandler';
|
|||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import PlatformPeg from '../../../PlatformPeg';
|
||||
import Modal from '../../../Modal';
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
|
||||
/**
|
||||
* This error boundary component can be used to wrap large content areas and
|
||||
|
@ -73,9 +74,10 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||
if (this.state.error) {
|
||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
||||
return <div className="mx_ErrorBoundary">
|
||||
<div className="mx_ErrorBoundary_body">
|
||||
<h1>{_t("Something went wrong!")}</h1>
|
||||
|
||||
let bugReportSection;
|
||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||
bugReportSection = <React.Fragment>
|
||||
<p>{_t(
|
||||
"Please <newIssueLink>create a new issue</newIssueLink> " +
|
||||
"on GitHub so that we can investigate this bug.", {}, {
|
||||
|
@ -94,6 +96,13 @@ export default class ErrorBoundary extends React.PureComponent {
|
|||
<AccessibleButton onClick={this._onBugReport} kind='primary'>
|
||||
{_t("Submit debug logs")}
|
||||
</AccessibleButton>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
return <div className="mx_ErrorBoundary">
|
||||
<div className="mx_ErrorBoundary_body">
|
||||
<h1>{_t("Something went wrong!")}</h1>
|
||||
{ bugReportSection }
|
||||
<AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
|
||||
{_t("Clear cache and reload")}
|
||||
</AccessibleButton>
|
||||
|
|
|
@ -19,6 +19,7 @@ import classNames from 'classnames';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
import Modal from '../../../Modal';
|
||||
import SdkConfig from "../../../SdkConfig";
|
||||
|
||||
export default class TileErrorBoundary extends React.Component {
|
||||
constructor(props) {
|
||||
|
@ -54,14 +55,20 @@ export default class TileErrorBoundary extends React.Component {
|
|||
mx_EventTile_content: true,
|
||||
mx_EventTile_tileError: true,
|
||||
};
|
||||
|
||||
let submitLogsButton;
|
||||
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||
submitLogsButton = <a onClick={this._onBugReport} href="#">
|
||||
{_t("Submit logs")}
|
||||
</a>;
|
||||
}
|
||||
|
||||
return (<div className={classNames(classes)}>
|
||||
<div className="mx_EventTile_line">
|
||||
<span>
|
||||
{_t("Can't load this message")}
|
||||
{ mxEvent && ` (${mxEvent.getType()})` }
|
||||
<a onClick={this._onBugReport} href="#">
|
||||
{_t("Submit logs")}
|
||||
</a>
|
||||
{ submitLogsButton }
|
||||
</span>
|
||||
</div>
|
||||
</div>);
|
||||
|
|
|
@ -42,6 +42,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
|||
import WidgetStore, {IApp} from "../../../stores/WidgetStore";
|
||||
import { E2EStatus } from "../../../utils/ShieldUtils";
|
||||
import RoomContext from "../../../contexts/RoomContext";
|
||||
import {UIFeature} from "../../../settings/UIFeature";
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -242,7 +243,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
|||
</Button>
|
||||
</Group>
|
||||
|
||||
<AppsSection room={room} />
|
||||
{ SettingsStore.getValue(UIFeature.Widgets) && <AppsSection room={room} /> }
|
||||
</BaseCard>;
|
||||
};
|
||||
|
||||
|
|
|
@ -952,30 +952,26 @@ function useRoomPermissions(cli, room, user) {
|
|||
|
||||
const PowerLevelSection = ({user, room, roomPermissions, powerLevels}) => {
|
||||
const [isEditing, setEditing] = useState(false);
|
||||
if (room && user.roomId) { // is in room
|
||||
if (isEditing) {
|
||||
return (<PowerLevelEditor
|
||||
user={user} room={room} roomPermissions={roomPermissions}
|
||||
onFinished={() => setEditing(false)} />);
|
||||
} else {
|
||||
const IconButton = sdk.getComponent('elements.IconButton');
|
||||
const powerLevelUsersDefault = powerLevels.users_default || 0;
|
||||
const powerLevel = parseInt(user.powerLevel, 10);
|
||||
const modifyButton = roomPermissions.canEdit ?
|
||||
(<IconButton icon="edit" onClick={() => setEditing(true)} />) : null;
|
||||
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
|
||||
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
|
||||
{role, roomName: room.name},
|
||||
{strong: label => <strong>{label}</strong>},
|
||||
);
|
||||
return (
|
||||
<div className="mx_UserInfo_profileField">
|
||||
<div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (isEditing) {
|
||||
return (<PowerLevelEditor
|
||||
user={user} room={room} roomPermissions={roomPermissions}
|
||||
onFinished={() => setEditing(false)} />);
|
||||
} else {
|
||||
return null;
|
||||
const IconButton = sdk.getComponent('elements.IconButton');
|
||||
const powerLevelUsersDefault = powerLevels.users_default || 0;
|
||||
const powerLevel = parseInt(user.powerLevel, 10);
|
||||
const modifyButton = roomPermissions.canEdit ?
|
||||
(<IconButton icon="edit" onClick={() => setEditing(true)} />) : null;
|
||||
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
|
||||
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
|
||||
{role, roomName: room.name},
|
||||
{strong: label => <strong>{label}</strong>},
|
||||
);
|
||||
return (
|
||||
<div className="mx_UserInfo_profileField">
|
||||
<div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1268,14 +1264,15 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
|
|||
spinner = <Loader imgClassName="mx_ContextualMenu_spinner" />;
|
||||
}
|
||||
|
||||
const memberDetails = (
|
||||
<PowerLevelSection
|
||||
let memberDetails;
|
||||
if (room && member.roomId) {
|
||||
memberDetails = <PowerLevelSection
|
||||
powerLevels={powerLevels}
|
||||
user={member}
|
||||
room={room}
|
||||
roomPermissions={roomPermissions}
|
||||
/>
|
||||
);
|
||||
/>;
|
||||
}
|
||||
|
||||
// only display the devices list if our client supports E2E
|
||||
const cryptoEnabled = cli.isCryptoEnabled();
|
||||
|
|
|
@ -28,6 +28,7 @@ import RateLimitedFunc from '../../../ratelimitedfunc';
|
|||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||
import CallView from "../voip/CallView";
|
||||
import {UIFeature} from "../../../settings/UIFeature";
|
||||
|
||||
|
||||
export default class AuxPanel extends React.Component {
|
||||
|
@ -198,18 +199,21 @@ export default class AuxPanel extends React.Component {
|
|||
/>
|
||||
);
|
||||
|
||||
const appsDrawer = <AppsDrawer
|
||||
room={this.props.room}
|
||||
userId={this.props.userId}
|
||||
maxHeight={this.props.maxHeight}
|
||||
showApps={this.props.showApps}
|
||||
hide={this.props.hideAppsDrawer}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
/>;
|
||||
let appsDrawer;
|
||||
if (SettingsStore.getValue(UIFeature.Widgets)) {
|
||||
appsDrawer = <AppsDrawer
|
||||
room={this.props.room}
|
||||
userId={this.props.userId}
|
||||
maxHeight={this.props.maxHeight}
|
||||
showApps={this.props.showApps}
|
||||
hide={this.props.hideAppsDrawer}
|
||||
resizeNotifier={this.props.resizeNotifier}
|
||||
/>;
|
||||
}
|
||||
|
||||
let stateViews = null;
|
||||
if (this.state.counters && SettingsStore.getValue("feature_state_counters")) {
|
||||
let counters = [];
|
||||
const counters = [];
|
||||
|
||||
this.state.counters.forEach((counter, idx) => {
|
||||
const title = counter.title;
|
||||
|
@ -218,7 +222,7 @@ export default class AuxPanel extends React.Component {
|
|||
const severity = counter.severity;
|
||||
const stateKey = counter.stateKey;
|
||||
|
||||
let span = <span>{ title }: { value }</span>
|
||||
let span = <span>{ title }: { value }</span>;
|
||||
|
||||
if (link) {
|
||||
span = (
|
||||
|
|
|
@ -31,6 +31,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
|||
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
|
||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||
import ReplyPreview from "./ReplyPreview";
|
||||
import {UIFeature} from "../../../settings/UIFeature";
|
||||
|
||||
function ComposerAvatar(props) {
|
||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||
|
@ -384,9 +385,12 @@ export default class MessageComposer extends React.Component {
|
|||
permalinkCreator={this.props.permalinkCreator} />,
|
||||
<UploadButton key="controls_upload" roomId={this.props.room.roomId} />,
|
||||
<EmojiButton key="emoji_button" addEmoji={this.addEmoji} />,
|
||||
<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />,
|
||||
);
|
||||
|
||||
if (SettingsStore.getValue(UIFeature.Widgets)) {
|
||||
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
|
||||
}
|
||||
|
||||
if (this.state.showCallButtons) {
|
||||
if (callInProgress) {
|
||||
controls.push(
|
||||
|
|
|
@ -37,6 +37,7 @@ import {abbreviateUrl} from "../../../../../utils/UrlUtils";
|
|||
import { getThreepidsWithBindStatus } from '../../../../../boundThreepids';
|
||||
import Spinner from "../../../elements/Spinner";
|
||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||
import {UIFeature} from "../../../../../settings/UIFeature";
|
||||
|
||||
export default class GeneralUserSettingsTab extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -366,6 +367,8 @@ export default class GeneralUserSettingsTab extends React.Component {
|
|||
}
|
||||
|
||||
_renderIntegrationManagerSection() {
|
||||
if (!SettingsStore.getValue(UIFeature.Widgets)) return null;
|
||||
|
||||
const SetIntegrationManager = sdk.getComponent("views.settings.SetIntegrationManager");
|
||||
|
||||
return (
|
||||
|
|
|
@ -329,6 +329,29 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
</div>;
|
||||
}
|
||||
|
||||
let privacySection;
|
||||
if (Analytics.canEnable()) {
|
||||
privacySection = <React.Fragment>
|
||||
<div className="mx_SettingsTab_heading">{_t("Privacy")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Analytics")}</span>
|
||||
<div className="mx_SettingsTab_subsectionText">
|
||||
{_t(
|
||||
"%(brand)s collects anonymous analytics to allow us to improve the application.",
|
||||
{ brand },
|
||||
)}
|
||||
|
||||
{_t("Privacy is important to us, so we don't collect any personal or " +
|
||||
"identifiable data for our analytics.")}
|
||||
<AccessibleButton className="mx_SettingsTab_linkBtn" onClick={Analytics.showDetailsModal}>
|
||||
{_t("Learn more about how we use analytics.")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
<SettingsFlag name="analyticsOptIn" level={SettingLevel.DEVICE} onChange={this._updateAnalytics} />
|
||||
</div>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_SecurityUserSettingsTab">
|
||||
{warning}
|
||||
|
@ -357,24 +380,7 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
{crossSigning}
|
||||
{this._renderCurrentDeviceInfo()}
|
||||
</div>
|
||||
<div className="mx_SettingsTab_heading">{_t("Privacy")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Analytics")}</span>
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
{_t(
|
||||
"%(brand)s collects anonymous analytics to allow us to improve the application.",
|
||||
{ brand },
|
||||
)}
|
||||
|
||||
{_t("Privacy is important to us, so we don't collect any personal or " +
|
||||
"identifiable data for our analytics.")}
|
||||
<AccessibleButton className="mx_SettingsTab_linkBtn" onClick={Analytics.showDetailsModal}>
|
||||
{_t("Learn more about how we use analytics.")}
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
<SettingsFlag name='analyticsOptIn' level={SettingLevel.DEVICE}
|
||||
onChange={this._updateAnalytics} />
|
||||
</div>
|
||||
{ privacySection }
|
||||
<div className="mx_SettingsTab_heading">{_t("Advanced")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
{this._renderIgnoredUsers()}
|
||||
|
|
|
@ -912,13 +912,13 @@
|
|||
"Message search": "Message search",
|
||||
"Cross-signing": "Cross-signing",
|
||||
"Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.": "Your server admin has disabled end-to-end encryption by default in private rooms & Direct Messages.",
|
||||
"Where you’re logged in": "Where you’re logged in",
|
||||
"Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.": "Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.",
|
||||
"A session's public name is visible to people you communicate with": "A session's public name is visible to people you communicate with",
|
||||
"Privacy": "Privacy",
|
||||
"%(brand)s collects anonymous analytics to allow us to improve the application.": "%(brand)s collects anonymous analytics to allow us to improve the application.",
|
||||
"Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.": "Privacy is important to us, so we don't collect any personal or identifiable data for our analytics.",
|
||||
"Learn more about how we use analytics.": "Learn more about how we use analytics.",
|
||||
"Where you’re logged in": "Where you’re logged in",
|
||||
"Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.": "Manage the names of and sign out of your sessions below or <a>verify them in your User Profile</a>.",
|
||||
"A session's public name is visible to people you communicate with": "A session's public name is visible to people you communicate with",
|
||||
"No media permissions": "No media permissions",
|
||||
"You may need to manually permit %(brand)s to access your microphone/webcam": "You may need to manually permit %(brand)s to access your microphone/webcam",
|
||||
"Missing media permissions, click the button below to request.": "Missing media permissions, click the button below to request.",
|
||||
|
@ -1440,8 +1440,8 @@
|
|||
"Click to view edits": "Click to view edits",
|
||||
"Edited at %(date)s. Click to view edits.": "Edited at %(date)s. Click to view edits.",
|
||||
"edited": "edited",
|
||||
"Can't load this message": "Can't load this message",
|
||||
"Submit logs": "Submit logs",
|
||||
"Can't load this message": "Can't load this message",
|
||||
"Failed to load group members": "Failed to load group members",
|
||||
"Filter community members": "Filter community members",
|
||||
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?",
|
||||
|
@ -2132,10 +2132,10 @@
|
|||
"Uploading %(filename)s and %(count)s others|zero": "Uploading %(filename)s",
|
||||
"Uploading %(filename)s and %(count)s others|one": "Uploading %(filename)s and %(count)s other",
|
||||
"Failed to find the general chat for this community": "Failed to find the general chat for this community",
|
||||
"Feedback": "Feedback",
|
||||
"Notification settings": "Notification settings",
|
||||
"Security & privacy": "Security & privacy",
|
||||
"All settings": "All settings",
|
||||
"Feedback": "Feedback",
|
||||
"Community settings": "Community settings",
|
||||
"User settings": "User settings",
|
||||
"Switch to light mode": "Switch to light mode",
|
||||
|
|
|
@ -588,6 +588,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
"showCallButtonsInComposer": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
||||
default: true,
|
||||
controller: new UIFeatureController(UIFeature.Voip),
|
||||
},
|
||||
"e2ee.manuallyVerifyAllSessions": {
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
|
||||
|
@ -618,6 +619,18 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
|||
supportedLevels: LEVELS_UI_FEATURE,
|
||||
default: true,
|
||||
},
|
||||
[UIFeature.Widgets]: {
|
||||
supportedLevels: LEVELS_UI_FEATURE,
|
||||
default: true,
|
||||
},
|
||||
[UIFeature.Voip]: {
|
||||
supportedLevels: LEVELS_UI_FEATURE,
|
||||
default: true,
|
||||
},
|
||||
[UIFeature.Feedback]: {
|
||||
supportedLevels: LEVELS_UI_FEATURE,
|
||||
default: true,
|
||||
},
|
||||
[UIFeature.Flair]: {
|
||||
supportedLevels: LEVELS_UI_FEATURE,
|
||||
default: true,
|
||||
|
|
|
@ -17,5 +17,8 @@ limitations under the License.
|
|||
// see settings.md for documentation on conventions
|
||||
export enum UIFeature {
|
||||
URLPreviews = "UIFeature.urlPreviews",
|
||||
Widgets = "UIFeature.widgets",
|
||||
Voip = "UIFeature.voip",
|
||||
Feedback = "UIFeature.feedback",
|
||||
Flair = "UIFeature.flair",
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue