mirror of
https://github.com/element-hq/element-web
synced 2024-11-29 04:48:50 +03:00
Merge branch 'develop' into travis/copy2
This commit is contained in:
commit
afec470ec3
65 changed files with 355 additions and 1650 deletions
|
@ -19,7 +19,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
|
|
||||||
overrides: [{
|
overrides: [{
|
||||||
"files": ["src/**/*.{ts, tsx}"],
|
"files": ["src/**/*.{ts,tsx}"],
|
||||||
"extends": ["matrix-org/ts"],
|
"extends": ["matrix-org/ts"],
|
||||||
"rules": {
|
"rules": {
|
||||||
// We disable this while we're transitioning
|
// We disable this while we're transitioning
|
||||||
|
|
69
CHANGELOG.md
69
CHANGELOG.md
|
@ -1,3 +1,72 @@
|
||||||
|
Changes in [3.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.3.0) (2020-09-01)
|
||||||
|
===================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.3.0-rc.1...v3.3.0)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 8.2.0
|
||||||
|
|
||||||
|
Changes in [3.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.3.0-rc.1) (2020-08-26)
|
||||||
|
=============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0...v3.3.0-rc.1)
|
||||||
|
|
||||||
|
* Upgrade to JS SDK 8.2.0-rc.1
|
||||||
|
* Update from Weblate
|
||||||
|
[\#5146](https://github.com/matrix-org/matrix-react-sdk/pull/5146)
|
||||||
|
* BaseAvatar avoid initial render with default avatar
|
||||||
|
[\#5142](https://github.com/matrix-org/matrix-react-sdk/pull/5142)
|
||||||
|
* Enforce Secure Backup completion when requested by HS
|
||||||
|
[\#5130](https://github.com/matrix-org/matrix-react-sdk/pull/5130)
|
||||||
|
* Communities v2 prototype: Explore rooms, global state, and default room
|
||||||
|
[\#5139](https://github.com/matrix-org/matrix-react-sdk/pull/5139)
|
||||||
|
* Add communities v2 prototyping feature flag + initial tag panel prototypes
|
||||||
|
[\#5133](https://github.com/matrix-org/matrix-react-sdk/pull/5133)
|
||||||
|
* Remove some unused components
|
||||||
|
[\#5134](https://github.com/matrix-org/matrix-react-sdk/pull/5134)
|
||||||
|
* Allow avatar image view for 1:1 rooms
|
||||||
|
[\#5137](https://github.com/matrix-org/matrix-react-sdk/pull/5137)
|
||||||
|
* Send mx_local_settings in rageshake
|
||||||
|
[\#5136](https://github.com/matrix-org/matrix-react-sdk/pull/5136)
|
||||||
|
* Run all room leaving behaviour through a single function
|
||||||
|
[\#5132](https://github.com/matrix-org/matrix-react-sdk/pull/5132)
|
||||||
|
* Add clarifying comment in media device selection
|
||||||
|
[\#5131](https://github.com/matrix-org/matrix-react-sdk/pull/5131)
|
||||||
|
* Settings v3: Feature flag changes
|
||||||
|
[\#5124](https://github.com/matrix-org/matrix-react-sdk/pull/5124)
|
||||||
|
* Clear url previews if they all get edited out of the event
|
||||||
|
[\#5129](https://github.com/matrix-org/matrix-react-sdk/pull/5129)
|
||||||
|
* Consider tab completions as modifications for editing purposes to unlock
|
||||||
|
sending
|
||||||
|
[\#5128](https://github.com/matrix-org/matrix-react-sdk/pull/5128)
|
||||||
|
* Use matrix-doc for SAS emoji translations
|
||||||
|
[\#5125](https://github.com/matrix-org/matrix-react-sdk/pull/5125)
|
||||||
|
* Add a rageshake function to download the logs locally
|
||||||
|
[\#3849](https://github.com/matrix-org/matrix-react-sdk/pull/3849)
|
||||||
|
* Room List filtering visual tweaks
|
||||||
|
[\#5123](https://github.com/matrix-org/matrix-react-sdk/pull/5123)
|
||||||
|
* Make reply preview not an overlay so you can see new messages
|
||||||
|
[\#5072](https://github.com/matrix-org/matrix-react-sdk/pull/5072)
|
||||||
|
* Allow room tile context menu when minimized using right click
|
||||||
|
[\#5113](https://github.com/matrix-org/matrix-react-sdk/pull/5113)
|
||||||
|
* Add null guard to group inviter for corrupted groups
|
||||||
|
[\#5121](https://github.com/matrix-org/matrix-react-sdk/pull/5121)
|
||||||
|
* Room List styling tweaks
|
||||||
|
[\#5118](https://github.com/matrix-org/matrix-react-sdk/pull/5118)
|
||||||
|
* Fix corner rounding on images not always affecting right side
|
||||||
|
[\#5120](https://github.com/matrix-org/matrix-react-sdk/pull/5120)
|
||||||
|
* Change add room action for rooms to context menu
|
||||||
|
[\#5108](https://github.com/matrix-org/matrix-react-sdk/pull/5108)
|
||||||
|
* Switch out the globe icon and colour it depending on theme
|
||||||
|
[\#5106](https://github.com/matrix-org/matrix-react-sdk/pull/5106)
|
||||||
|
* Message Action Bar watch for event send changes
|
||||||
|
[\#5115](https://github.com/matrix-org/matrix-react-sdk/pull/5115)
|
||||||
|
* Put message previews for Emoji behind Labs
|
||||||
|
[\#5110](https://github.com/matrix-org/matrix-react-sdk/pull/5110)
|
||||||
|
* Fix styling for selected community marker
|
||||||
|
[\#5107](https://github.com/matrix-org/matrix-react-sdk/pull/5107)
|
||||||
|
* Fix action bar safe area regression
|
||||||
|
[\#5111](https://github.com/matrix-org/matrix-react-sdk/pull/5111)
|
||||||
|
* Fix /op slash command
|
||||||
|
[\#5109](https://github.com/matrix-org/matrix-react-sdk/pull/5109)
|
||||||
|
|
||||||
Changes in [3.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.2.0) (2020-08-17)
|
Changes in [3.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.2.0) (2020-08-17)
|
||||||
===================================================================================================
|
===================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0-rc.1...v3.2.0)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0-rc.1...v3.2.0)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "3.2.0",
|
"version": "3.3.0",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -163,9 +163,7 @@
|
||||||
"stylelint-config-standard": "^18.3.0",
|
"stylelint-config-standard": "^18.3.0",
|
||||||
"stylelint-scss": "^3.18.0",
|
"stylelint-scss": "^3.18.0",
|
||||||
"typescript": "^3.9.7",
|
"typescript": "^3.9.7",
|
||||||
"walk": "^2.3.14",
|
"walk": "^2.3.14"
|
||||||
"webpack": "^4.43.0",
|
|
||||||
"webpack-cli": "^3.3.12"
|
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"testMatch": [
|
"testMatch": [
|
||||||
|
|
|
@ -70,6 +70,7 @@ interface IContent {
|
||||||
|
|
||||||
interface IThumbnail {
|
interface IThumbnail {
|
||||||
info: {
|
info: {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
thumbnail_info: {
|
thumbnail_info: {
|
||||||
w: number;
|
w: number;
|
||||||
h: number;
|
h: number;
|
||||||
|
@ -104,7 +105,12 @@ interface IAbortablePromise<T> extends Promise<T> {
|
||||||
* @return {Promise} A promise that resolves with an object with an info key
|
* @return {Promise} A promise that resolves with an object with an info key
|
||||||
* and a thumbnail key.
|
* and a thumbnail key.
|
||||||
*/
|
*/
|
||||||
function createThumbnail(element: ThumbnailableElement, inputWidth: number, inputHeight: number, mimeType: string): Promise<IThumbnail> {
|
function createThumbnail(
|
||||||
|
element: ThumbnailableElement,
|
||||||
|
inputWidth: number,
|
||||||
|
inputHeight: number,
|
||||||
|
mimeType: string,
|
||||||
|
): Promise<IThumbnail> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let targetWidth = inputWidth;
|
let targetWidth = inputWidth;
|
||||||
let targetHeight = inputHeight;
|
let targetHeight = inputHeight;
|
||||||
|
@ -437,11 +443,13 @@ export default class ContentMessages {
|
||||||
for (let i = 0; i < okFiles.length; ++i) {
|
for (let i = 0; i < okFiles.length; ++i) {
|
||||||
const file = okFiles[i];
|
const file = okFiles[i];
|
||||||
if (!uploadAll) {
|
if (!uploadAll) {
|
||||||
const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation', '', UploadConfirmDialog, {
|
const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
|
||||||
file,
|
'', UploadConfirmDialog, {
|
||||||
currentIndex: i,
|
file,
|
||||||
totalFiles: okFiles.length,
|
currentIndex: i,
|
||||||
});
|
totalFiles: okFiles.length,
|
||||||
|
},
|
||||||
|
);
|
||||||
const [shouldContinue, shouldUploadAll] = await finished;
|
const [shouldContinue, shouldUploadAll] = await finished;
|
||||||
if (!shouldContinue) break;
|
if (!shouldContinue) break;
|
||||||
if (shouldUploadAll) {
|
if (shouldUploadAll) {
|
||||||
|
|
|
@ -339,33 +339,9 @@ class HtmlHighlighter extends BaseHighlighter<string> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TextHighlighter extends BaseHighlighter<React.ReactNode> {
|
|
||||||
private key = 0;
|
|
||||||
|
|
||||||
/* create a <span> node to hold the given content
|
|
||||||
*
|
|
||||||
* snippet: content of the span
|
|
||||||
* highlight: true to highlight as a search match
|
|
||||||
*
|
|
||||||
* returns a React node
|
|
||||||
*/
|
|
||||||
protected processSnippet(snippet: string, highlight: boolean): React.ReactNode {
|
|
||||||
const key = this.key++;
|
|
||||||
|
|
||||||
let node = <span key={key} className={highlight ? this.highlightClass : null}>
|
|
||||||
{ snippet }
|
|
||||||
</span>;
|
|
||||||
|
|
||||||
if (highlight && this.highlightLink) {
|
|
||||||
node = <a key={key} href={this.highlightLink}>{ node }</a>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IContent {
|
interface IContent {
|
||||||
format?: string;
|
format?: string;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
formatted_body?: string;
|
formatted_body?: string;
|
||||||
body: string;
|
body: string;
|
||||||
}
|
}
|
||||||
|
@ -474,8 +450,13 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
|
||||||
});
|
});
|
||||||
|
|
||||||
return isDisplayedWithHtml ?
|
return isDisplayedWithHtml ?
|
||||||
<span key="body" ref={opts.ref} className={className} dangerouslySetInnerHTML={{ __html: safeBody }} dir="auto" /> :
|
<span
|
||||||
<span key="body" ref={opts.ref} className={className} dir="auto">{ strippedBody }</span>;
|
key="body"
|
||||||
|
ref={opts.ref}
|
||||||
|
className={className}
|
||||||
|
dangerouslySetInnerHTML={{ __html: safeBody }}
|
||||||
|
dir="auto"
|
||||||
|
/> : <span key="body" ref={opts.ref} className={className} dir="auto">{ strippedBody }</span>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -151,7 +151,7 @@ export class ModalManager {
|
||||||
prom: Promise<React.ComponentType>,
|
prom: Promise<React.ComponentType>,
|
||||||
props?: IProps<T>,
|
props?: IProps<T>,
|
||||||
className?: string,
|
className?: string,
|
||||||
options?: IOptions<T>
|
options?: IOptions<T>,
|
||||||
) {
|
) {
|
||||||
const modal: IModal<T> = {
|
const modal: IModal<T> = {
|
||||||
onFinished: props ? props.onFinished : null,
|
onFinished: props ? props.onFinished : null,
|
||||||
|
@ -182,7 +182,7 @@ export class ModalManager {
|
||||||
|
|
||||||
private getCloseFn<T extends any[]>(
|
private getCloseFn<T extends any[]>(
|
||||||
modal: IModal<T>,
|
modal: IModal<T>,
|
||||||
props: IProps<T>
|
props: IProps<T>,
|
||||||
): [IHandle<T>["close"], IHandle<T>["finished"]] {
|
): [IHandle<T>["close"], IHandle<T>["finished"]] {
|
||||||
const deferred = defer<T>();
|
const deferred = defer<T>();
|
||||||
return [async (...args: T) => {
|
return [async (...args: T) => {
|
||||||
|
@ -264,7 +264,7 @@ export class ModalManager {
|
||||||
className?: string,
|
className?: string,
|
||||||
isPriorityModal = false,
|
isPriorityModal = false,
|
||||||
isStaticModal = false,
|
isStaticModal = false,
|
||||||
options: IOptions<T> = {}
|
options: IOptions<T> = {},
|
||||||
): IHandle<T> {
|
): IHandle<T> {
|
||||||
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, options);
|
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, options);
|
||||||
if (isPriorityModal) {
|
if (isPriorityModal) {
|
||||||
|
@ -287,7 +287,7 @@ export class ModalManager {
|
||||||
private appendDialogAsync<T extends any[]>(
|
private appendDialogAsync<T extends any[]>(
|
||||||
prom: Promise<React.ComponentType>,
|
prom: Promise<React.ComponentType>,
|
||||||
props?: IProps<T>,
|
props?: IProps<T>,
|
||||||
className?: string
|
className?: string,
|
||||||
): IHandle<T> {
|
): IHandle<T> {
|
||||||
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, {});
|
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, {});
|
||||||
|
|
||||||
|
|
|
@ -860,12 +860,12 @@ export const Commands = [
|
||||||
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
|
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
|
||||||
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
|
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
|
||||||
'"%(fingerprint)s". This could mean your communications are being intercepted!',
|
'"%(fingerprint)s". This could mean your communications are being intercepted!',
|
||||||
{
|
{
|
||||||
fprint,
|
fprint,
|
||||||
userId,
|
userId,
|
||||||
deviceId,
|
deviceId,
|
||||||
fingerprint,
|
fingerprint,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
await cli.setDeviceVerified(userId, deviceId, true);
|
await cli.setDeviceVerified(userId, deviceId, true);
|
||||||
|
@ -879,7 +879,7 @@ export const Commands = [
|
||||||
{
|
{
|
||||||
_t('The signing key you provided matches the signing key you received ' +
|
_t('The signing key you provided matches the signing key you received ' +
|
||||||
'from %(userId)s\'s session %(deviceId)s. Session marked as verified.',
|
'from %(userId)s\'s session %(deviceId)s. Session marked as verified.',
|
||||||
{userId, deviceId})
|
{userId, deviceId})
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
</div>,
|
</div>,
|
||||||
|
|
|
@ -168,7 +168,7 @@ const shortcuts: Record<Categories, IShortcut[]> = {
|
||||||
key: Key.U,
|
key: Key.U,
|
||||||
}],
|
}],
|
||||||
description: _td("Upload a file"),
|
description: _td("Upload a file"),
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
[Categories.ROOM_LIST]: [
|
[Categories.ROOM_LIST]: [
|
||||||
|
|
|
@ -190,7 +190,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
} else if (onKeyDown) {
|
} else if (onKeyDown) {
|
||||||
return onKeyDown(ev, state);
|
return onKeyDown(ev, context.state);
|
||||||
}
|
}
|
||||||
}, [context.state, onKeyDown, handleHomeEnd]);
|
}, [context.state, onKeyDown, handleHomeEnd]);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ const Toolbar: React.FC<IProps> = ({children, ...props}) => {
|
||||||
const target = ev.target as HTMLElement;
|
const target = ev.target as HTMLElement;
|
||||||
let handled = true;
|
let handled = true;
|
||||||
|
|
||||||
|
// HOME and END are handled by RovingTabIndexProvider
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.ARROW_UP:
|
case Key.ARROW_UP:
|
||||||
case Key.ARROW_DOWN:
|
case Key.ARROW_DOWN:
|
||||||
|
@ -47,8 +48,6 @@ const Toolbar: React.FC<IProps> = ({children, ...props}) => {
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// HOME and END are handled by RovingTabIndexProvider
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
handled = false;
|
handled = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ import React from "react";
|
||||||
|
|
||||||
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../../components/views/elements/AccessibleTooltipButton";
|
||||||
|
|
||||||
interface IProps extends React.ComponentProps<typeof AccessibleTooltipButton> {
|
interface IProps extends React.ComponentProps<typeof AccessibleTooltipButton> {
|
||||||
// whether or not the context menu is currently open
|
// whether or not the context menu is currently open
|
||||||
isExpanded: boolean;
|
isExpanded: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ import AccessibleTooltipButton from "../../components/views/elements/AccessibleT
|
||||||
import {useRovingTabIndex} from "../RovingTabIndex";
|
import {useRovingTabIndex} from "../RovingTabIndex";
|
||||||
import {Ref} from "./types";
|
import {Ref} from "./types";
|
||||||
|
|
||||||
interface IProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "onFocus" | "inputRef" | "tabIndex"> {
|
type ATBProps = React.ComponentProps<typeof AccessibleTooltipButton>;
|
||||||
|
interface IProps extends Omit<ATBProps, "onFocus" | "inputRef" | "tabIndex"> {
|
||||||
inputRef?: Ref;
|
inputRef?: Ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,6 @@ limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import AccessibleButton from "../../components/views/elements/AccessibleButton";
|
|
||||||
import {useRovingTabIndex} from "../RovingTabIndex";
|
import {useRovingTabIndex} from "../RovingTabIndex";
|
||||||
import {FocusHandler, Ref} from "./types";
|
import {FocusHandler, Ref} from "./types";
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,11 @@ export default class CommandProvider extends AutocompleteProvider {
|
||||||
|
|
||||||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="mx_Autocomplete_Completion_container_block" role="listbox" aria-label={_t("Command Autocomplete")}>
|
<div
|
||||||
|
className="mx_Autocomplete_Completion_container_block"
|
||||||
|
role="listbox"
|
||||||
|
aria-label={_t("Command Autocomplete")}
|
||||||
|
>
|
||||||
{ completions }
|
{ completions }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -91,15 +91,15 @@ export default class CommunityProvider extends AutocompleteProvider {
|
||||||
href: makeGroupPermalink(groupId),
|
href: makeGroupPermalink(groupId),
|
||||||
component: (
|
component: (
|
||||||
<PillCompletion title={name} description={groupId}>
|
<PillCompletion title={name} description={groupId}>
|
||||||
<BaseAvatar name={name || groupId}
|
<BaseAvatar
|
||||||
width={24}
|
name={name || groupId}
|
||||||
height={24}
|
width={24}
|
||||||
url={avatarUrl ? cli.mxcUrlToHttp(avatarUrl, 24, 24) : null} />
|
height={24}
|
||||||
|
url={avatarUrl ? cli.mxcUrlToHttp(avatarUrl, 24, 24) : null} />
|
||||||
</PillCompletion>
|
</PillCompletion>
|
||||||
),
|
),
|
||||||
range,
|
range,
|
||||||
}))
|
})).slice(0, 4);
|
||||||
.slice(0, 4);
|
|
||||||
}
|
}
|
||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,9 @@ export const TextualCompletion = forwardRef<ITextualCompletionProps, any>((props
|
||||||
const {title, subtitle, description, className, ...restProps} = props;
|
const {title, subtitle, description, className, ...restProps} = props;
|
||||||
return (
|
return (
|
||||||
<div {...restProps}
|
<div {...restProps}
|
||||||
className={classNames('mx_Autocomplete_Completion_block', className)}
|
className={classNames('mx_Autocomplete_Completion_block', className)}
|
||||||
role="option"
|
role="option"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
||||||
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
<span className="mx_Autocomplete_Completion_subtitle">{ subtitle }</span>
|
||||||
|
@ -53,9 +53,9 @@ export const PillCompletion = forwardRef<IPillCompletionProps, any>((props, ref)
|
||||||
const {title, subtitle, description, className, children, ...restProps} = props;
|
const {title, subtitle, description, className, children, ...restProps} = props;
|
||||||
return (
|
return (
|
||||||
<div {...restProps}
|
<div {...restProps}
|
||||||
className={classNames('mx_Autocomplete_Completion_pill', className)}
|
className={classNames('mx_Autocomplete_Completion_pill', className)}
|
||||||
role="option"
|
role="option"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
>
|
>
|
||||||
{ children }
|
{ children }
|
||||||
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
<span className="mx_Autocomplete_Completion_title">{ title }</span>
|
||||||
|
|
|
@ -139,7 +139,11 @@ export default class EmojiProvider extends AutocompleteProvider {
|
||||||
|
|
||||||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("Emoji Autocomplete")}>
|
<div
|
||||||
|
className="mx_Autocomplete_Completion_container_pill"
|
||||||
|
role="listbox"
|
||||||
|
aria-label={_t("Emoji Autocomplete")}
|
||||||
|
>
|
||||||
{ completions }
|
{ completions }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -110,9 +110,7 @@ export default class RoomProvider extends AutocompleteProvider {
|
||||||
),
|
),
|
||||||
range,
|
range,
|
||||||
};
|
};
|
||||||
})
|
}).filter((completion) => !!completion.completion && completion.completion.length > 0).slice(0, 4);
|
||||||
.filter((completion) => !!completion.completion && completion.completion.length > 0)
|
|
||||||
.slice(0, 4);
|
|
||||||
}
|
}
|
||||||
return completions;
|
return completions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,8 +71,13 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onRoomTimeline = (ev: MatrixEvent, room: Room, toStartOfTimeline: boolean, removed: boolean,
|
private onRoomTimeline = (
|
||||||
data: IRoomTimelineData) => {
|
ev: MatrixEvent,
|
||||||
|
room: Room,
|
||||||
|
toStartOfTimeline: boolean,
|
||||||
|
removed: boolean,
|
||||||
|
data: IRoomTimelineData,
|
||||||
|
) => {
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
if (removed) return;
|
if (removed) return;
|
||||||
if (room.roomId !== this.room.roomId) return;
|
if (room.roomId !== this.room.roomId) return;
|
||||||
|
@ -171,7 +176,11 @@ export default class UserProvider extends AutocompleteProvider {
|
||||||
|
|
||||||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||||
return (
|
return (
|
||||||
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("User Autocomplete")}>
|
<div
|
||||||
|
className="mx_Autocomplete_Completion_container_pill"
|
||||||
|
role="listbox"
|
||||||
|
aria-label={_t("User Autocomplete")}
|
||||||
|
>
|
||||||
{ completions }
|
{ completions }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
|
||||||
Copyright 2019, 2020 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../languageHandler';
|
|
||||||
import SdkConfig from '../../SdkConfig';
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'CompatibilityPage',
|
|
||||||
propTypes: {
|
|
||||||
onAccept: PropTypes.func,
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
onAccept: function() {}, // NOP
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onAccept: function() {
|
|
||||||
this.props.onAccept();
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const brand = SdkConfig.get().brand;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx_CompatibilityPage">
|
|
||||||
<div className="mx_CompatibilityPage_box">
|
|
||||||
<p>{_t(
|
|
||||||
"Sorry, your browser is <b>not</b> able to run %(brand)s.",
|
|
||||||
{
|
|
||||||
brand,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'b': (sub) => <b>{sub}</b>,
|
|
||||||
})
|
|
||||||
}</p>
|
|
||||||
<p>
|
|
||||||
{ _t(
|
|
||||||
"%(brand)s uses many advanced browser features, some of which are not available " +
|
|
||||||
"or experimental in your current browser.",
|
|
||||||
{ brand },
|
|
||||||
) }
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{ _t(
|
|
||||||
'Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ' +
|
|
||||||
'or <safariLink>Safari</safariLink> for the best experience.',
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
'chromeLink': (sub) => <a href="https://www.google.com/chrome">{sub}</a>,
|
|
||||||
'firefoxLink': (sub) => <a href="https://firefox.com">{sub}</a>,
|
|
||||||
'safariLink': (sub) => <a href="https://apple.com/safari">{sub}</a>,
|
|
||||||
},
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{ _t(
|
|
||||||
"With your current browser, the look and feel of the application may be " +
|
|
||||||
"completely incorrect, and some or all features may not function. " +
|
|
||||||
"If you want to try it anyway you can continue, but you are on your own in terms " +
|
|
||||||
"of any issues you may encounter!",
|
|
||||||
) }
|
|
||||||
</p>
|
|
||||||
<button onClick={this.onAccept}>
|
|
||||||
{ _t("I understand the risks and wish to continue") }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -233,8 +233,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
|
||||||
switch (ev.key) {
|
switch (ev.key) {
|
||||||
case Key.TAB:
|
case Key.TAB:
|
||||||
case Key.ESCAPE:
|
case Key.ESCAPE:
|
||||||
// close on left and right arrows too for when it is a context menu on a <Toolbar />
|
case Key.ARROW_LEFT: // close on left and right arrows too for when it is a context menu on a <Toolbar />
|
||||||
case Key.ARROW_LEFT:
|
|
||||||
case Key.ARROW_RIGHT:
|
case Key.ARROW_RIGHT:
|
||||||
this.props.onFinished();
|
this.props.onFinished();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -377,7 +377,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
|
||||||
public render(): React.ReactNode {
|
public render(): React.ReactNode {
|
||||||
const tagPanel = !this.state.showTagPanel ? null : (
|
const tagPanel = !this.state.showTagPanel ? null : (
|
||||||
<div className="mx_LeftPanel_tagPanelContainer">
|
<div className="mx_LeftPanel_tagPanelContainer">
|
||||||
<TagPanel/>
|
<TagPanel />
|
||||||
{SettingsStore.getValue("feature_custom_tags") ? <CustomRoomTagPanel /> : null}
|
{SettingsStore.getValue("feature_custom_tags") ? <CustomRoomTagPanel /> : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -43,11 +43,11 @@ import PlatformPeg from "../../PlatformPeg";
|
||||||
import { DefaultTagID } from "../../stores/room-list/models";
|
import { DefaultTagID } from "../../stores/room-list/models";
|
||||||
import {
|
import {
|
||||||
showToast as showSetPasswordToast,
|
showToast as showSetPasswordToast,
|
||||||
hideToast as hideSetPasswordToast
|
hideToast as hideSetPasswordToast,
|
||||||
} from "../../toasts/SetPasswordToast";
|
} from "../../toasts/SetPasswordToast";
|
||||||
import {
|
import {
|
||||||
showToast as showServerLimitToast,
|
showToast as showServerLimitToast,
|
||||||
hideToast as hideServerLimitToast
|
hideToast as hideServerLimitToast,
|
||||||
} from "../../toasts/ServerLimitToast";
|
} from "../../toasts/ServerLimitToast";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import LeftPanel from "./LeftPanel";
|
import LeftPanel from "./LeftPanel";
|
||||||
|
@ -79,6 +79,7 @@ interface IProps {
|
||||||
initialEventPixelOffset: number;
|
initialEventPixelOffset: number;
|
||||||
leftDisabled: boolean;
|
leftDisabled: boolean;
|
||||||
rightDisabled: boolean;
|
rightDisabled: boolean;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
page_type: string;
|
page_type: string;
|
||||||
autoJoin: boolean;
|
autoJoin: boolean;
|
||||||
thirdPartyInvite?: object;
|
thirdPartyInvite?: object;
|
||||||
|
@ -98,7 +99,9 @@ interface IProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IUsageLimit {
|
interface IUsageLimit {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
limit_type: "monthly_active_user" | string;
|
limit_type: "monthly_active_user" | string;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
admin_contact?: string;
|
admin_contact?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,10 +319,10 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
_calculateServerLimitToast(syncErrorData: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
|
_calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
|
||||||
const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
|
const error = syncError && syncError.error && syncError.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
|
||||||
if (error) {
|
if (error) {
|
||||||
usageLimitEventContent = syncErrorData.error.data;
|
usageLimitEventContent = syncError.error.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usageLimitEventContent) {
|
if (usageLimitEventContent) {
|
||||||
|
@ -620,18 +623,18 @@ class LoggedInView extends React.Component<IProps, IState> {
|
||||||
switch (this.props.page_type) {
|
switch (this.props.page_type) {
|
||||||
case PageTypes.RoomView:
|
case PageTypes.RoomView:
|
||||||
pageElement = <RoomView
|
pageElement = <RoomView
|
||||||
ref={this._roomView}
|
ref={this._roomView}
|
||||||
autoJoin={this.props.autoJoin}
|
autoJoin={this.props.autoJoin}
|
||||||
onRegistered={this.props.onRegistered}
|
onRegistered={this.props.onRegistered}
|
||||||
thirdPartyInvite={this.props.thirdPartyInvite}
|
thirdPartyInvite={this.props.thirdPartyInvite}
|
||||||
oobData={this.props.roomOobData}
|
oobData={this.props.roomOobData}
|
||||||
viaServers={this.props.viaServers}
|
viaServers={this.props.viaServers}
|
||||||
eventPixelOffset={this.props.initialEventPixelOffset}
|
eventPixelOffset={this.props.initialEventPixelOffset}
|
||||||
key={this.props.currentRoomId || 'roomview'}
|
key={this.props.currentRoomId || 'roomview'}
|
||||||
disabled={this.props.middleDisabled}
|
disabled={this.props.middleDisabled}
|
||||||
ConferenceHandler={this.props.ConferenceHandler}
|
ConferenceHandler={this.props.ConferenceHandler}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
/>;
|
/>;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PageTypes.MyGroups:
|
case PageTypes.MyGroups:
|
||||||
|
|
|
@ -69,7 +69,7 @@ import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
import {
|
import {
|
||||||
showToast as showAnalyticsToast,
|
showToast as showAnalyticsToast,
|
||||||
hideToast as hideAnalyticsToast
|
hideToast as hideAnalyticsToast,
|
||||||
} from "../../toasts/AnalyticsToast";
|
} from "../../toasts/AnalyticsToast";
|
||||||
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
|
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
|
||||||
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
|
||||||
|
@ -129,6 +129,7 @@ interface IScreen {
|
||||||
params?: object;
|
params?: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable camelcase */
|
||||||
interface IRoomInfo {
|
interface IRoomInfo {
|
||||||
room_id?: string;
|
room_id?: string;
|
||||||
room_alias?: string;
|
room_alias?: string;
|
||||||
|
@ -140,6 +141,7 @@ interface IRoomInfo {
|
||||||
oob_data?: object;
|
oob_data?: object;
|
||||||
via_servers?: string[];
|
via_servers?: string[];
|
||||||
}
|
}
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
interface IProps { // TODO type things better
|
interface IProps { // TODO type things better
|
||||||
config: Record<string, any>;
|
config: Record<string, any>;
|
||||||
|
@ -165,6 +167,7 @@ interface IState {
|
||||||
// the master view we are showing.
|
// the master view we are showing.
|
||||||
view: Views;
|
view: Views;
|
||||||
// What the LoggedInView would be showing if visible
|
// What the LoggedInView would be showing if visible
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
page_type?: PageTypes;
|
page_type?: PageTypes;
|
||||||
// The ID of the room we're viewing. This is either populated directly
|
// The ID of the room we're viewing. This is either populated directly
|
||||||
// in the case where we view a room by ID or by RoomView when it resolves
|
// in the case where we view a room by ID or by RoomView when it resolves
|
||||||
|
@ -180,8 +183,11 @@ interface IState {
|
||||||
middleDisabled: boolean;
|
middleDisabled: boolean;
|
||||||
// the right panel's disabled state is tracked in its store.
|
// the right panel's disabled state is tracked in its store.
|
||||||
// Parameters used in the registration dance with the IS
|
// Parameters used in the registration dance with the IS
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
register_client_secret?: string;
|
register_client_secret?: string;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
register_session_id?: string;
|
register_session_id?: string;
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
register_id_sid?: string;
|
register_id_sid?: string;
|
||||||
// When showing Modal dialogs we need to set aria-hidden on the root app element
|
// When showing Modal dialogs we need to set aria-hidden on the root app element
|
||||||
// and disable it when there are no dialogs
|
// and disable it when there are no dialogs
|
||||||
|
@ -341,6 +347,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
|
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
UNSAFE_componentWillUpdate(props, state) {
|
UNSAFE_componentWillUpdate(props, state) {
|
||||||
if (this.shouldTrackPageChange(this.state, state)) {
|
if (this.shouldTrackPageChange(this.state, state)) {
|
||||||
this.startPageChangeTimer();
|
this.startPageChangeTimer();
|
||||||
|
@ -610,8 +617,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
|
||||||
Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
|
Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
|
||||||
{initialTabId: tabPayload.initialTabId},
|
{initialTabId: tabPayload.initialTabId},
|
||||||
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true
|
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
);
|
|
||||||
|
|
||||||
// View the welcome or home page if we need something to look at
|
// View the welcome or home page if we need something to look at
|
||||||
this.viewSomethingBehindModal();
|
this.viewSomethingBehindModal();
|
||||||
|
@ -1080,7 +1086,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
title: _t("Leave room"),
|
title: _t("Leave room"),
|
||||||
description: (
|
description: (
|
||||||
<span>
|
<span>
|
||||||
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
|
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
|
||||||
{ warnings }
|
{ warnings }
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
|
@ -1433,7 +1439,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
cli.on("crypto.warning", (type) => {
|
cli.on("crypto.warning", (type) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'CRYPTO_WARNING_OLD_VERSION_DETECTED':
|
case 'CRYPTO_WARNING_OLD_VERSION_DETECTED':
|
||||||
const brand = SdkConfig.get().brand;
|
|
||||||
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
|
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
|
||||||
title: _t('Old cryptography data detected'),
|
title: _t('Old cryptography data detected'),
|
||||||
description: _t(
|
description: _t(
|
||||||
|
@ -1444,7 +1449,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
"in this version. This may also cause messages exchanged with this " +
|
"in this version. This may also cause messages exchanged with this " +
|
||||||
"version to fail. If you experience problems, log out and back in " +
|
"version to fail. If you experience problems, log out and back in " +
|
||||||
"again. To retain message history, export and re-import your keys.",
|
"again. To retain message history, export and re-import your keys.",
|
||||||
{ brand },
|
{ brand: SdkConfig.get().brand },
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -20,7 +20,6 @@ import classNames from "classnames";
|
||||||
import defaultDispatcher from "../../dispatcher/dispatcher";
|
import defaultDispatcher from "../../dispatcher/dispatcher";
|
||||||
import { _t } from "../../languageHandler";
|
import { _t } from "../../languageHandler";
|
||||||
import { ActionPayload } from "../../dispatcher/payloads";
|
import { ActionPayload } from "../../dispatcher/payloads";
|
||||||
import { throttle } from 'lodash';
|
|
||||||
import { Key } from "../../Keyboard";
|
import { Key } from "../../Keyboard";
|
||||||
import AccessibleButton from "../views/elements/AccessibleButton";
|
import AccessibleButton from "../views/elements/AccessibleButton";
|
||||||
import { Action } from "../../dispatcher/actions";
|
import { Action } from "../../dispatcher/actions";
|
||||||
|
@ -137,7 +136,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let icon = (
|
let icon = (
|
||||||
<div className='mx_RoomSearch_icon'/>
|
<div className='mx_RoomSearch_icon' />
|
||||||
);
|
);
|
||||||
let input = (
|
let input = (
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {_t} from '../../languageHandler';
|
import {_t} from '../../languageHandler';
|
||||||
import * as PropTypes from "prop-types";
|
|
||||||
import * as sdk from "../../index";
|
import * as sdk from "../../index";
|
||||||
import AutoHideScrollbar from './AutoHideScrollbar';
|
import AutoHideScrollbar from './AutoHideScrollbar';
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
|
|
|
@ -40,7 +40,7 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
|
||||||
import { SettingLevel } from "../../settings/SettingLevel";
|
import { SettingLevel } from "../../settings/SettingLevel";
|
||||||
import IconizedContextMenu, {
|
import IconizedContextMenu, {
|
||||||
IconizedContextMenuOption,
|
IconizedContextMenuOption,
|
||||||
IconizedContextMenuOptionList
|
IconizedContextMenuOptionList,
|
||||||
} from "../views/context_menus/IconizedContextMenu";
|
} from "../views/context_menus/IconizedContextMenu";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
|
@ -234,12 +234,12 @@ export default class UserMenu extends React.Component<IProps, IState> {
|
||||||
>
|
>
|
||||||
<div className="mx_UserMenu_contextMenu_header">
|
<div className="mx_UserMenu_contextMenu_header">
|
||||||
<div className="mx_UserMenu_contextMenu_name">
|
<div className="mx_UserMenu_contextMenu_name">
|
||||||
<span className="mx_UserMenu_contextMenu_displayName">
|
<span className="mx_UserMenu_contextMenu_displayName">
|
||||||
{OwnProfileStore.instance.displayName}
|
{OwnProfileStore.instance.displayName}
|
||||||
</span>
|
</span>
|
||||||
<span className="mx_UserMenu_contextMenu_userId">
|
<span className="mx_UserMenu_contextMenu_userId">
|
||||||
{MatrixClientPeg.get().getUserId()}
|
{MatrixClientPeg.get().getUserId()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
className="mx_UserMenu_contextMenu_themeButton"
|
className="mx_UserMenu_contextMenu_themeButton"
|
||||||
|
|
|
@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react';
|
import React, {useCallback, useContext, useEffect, useState} from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import * as AvatarLogic from '../../../Avatar';
|
import * as AvatarLogic from '../../../Avatar';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
|
@ -96,7 +96,7 @@ const BaseAvatar = (props: IProps) => {
|
||||||
urls,
|
urls,
|
||||||
width = 40,
|
width = 40,
|
||||||
height = 40,
|
height = 40,
|
||||||
resizeMethod = "crop", // eslint-disable-line no-unused-vars
|
resizeMethod = "crop", // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
defaultToInitialLetter = true,
|
defaultToInitialLetter = true,
|
||||||
onClick,
|
onClick,
|
||||||
inputRef,
|
inputRef,
|
||||||
|
|
|
@ -126,7 +126,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
|
||||||
private onPresenceUpdate = () => {
|
private onPresenceUpdate = () => {
|
||||||
if (this.isUnmounted) return;
|
if (this.isUnmounted) return;
|
||||||
|
|
||||||
let newIcon = this.getPresenceIcon();
|
const newIcon = this.getPresenceIcon();
|
||||||
if (newIcon !== this.state.icon) this.setState({icon: newIcon});
|
if (newIcon !== this.state.icon) this.setState({icon: newIcon});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default class GroupAvatar extends React.Component<IProps> {
|
||||||
render() {
|
render() {
|
||||||
// extract the props we use from props so we can pass any others through
|
// extract the props we use from props so we can pass any others through
|
||||||
// should consider adding this as a global rule in js-sdk?
|
// should consider adding this as a global rule in js-sdk?
|
||||||
/*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/
|
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||||
const {groupId, groupAvatarUrl, groupName, ...otherProps} = this.props;
|
const {groupId, groupAvatarUrl, groupName, ...otherProps} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -25,4 +25,4 @@ const PulsedAvatar: React.FC<IProps> = (props) => {
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PulsedAvatar;
|
export default PulsedAvatar;
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from "react";
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
|
|
||||||
const Presets = {
|
|
||||||
PrivateChat: "private_chat",
|
|
||||||
PublicChat: "public_chat",
|
|
||||||
Custom: "custom",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'CreateRoomPresets',
|
|
||||||
propTypes: {
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
preset: PropTypes.string,
|
|
||||||
},
|
|
||||||
|
|
||||||
Presets: Presets,
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
onChange: function() {},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
onValueChanged: function(ev) {
|
|
||||||
this.props.onChange(ev.target.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
|
|
||||||
<option value={this.Presets.PrivateChat}>{ _t("Private Chat") }</option>
|
|
||||||
<option value={this.Presets.PublicChat}>{ _t("Public Chat") }</option>
|
|
||||||
<option value={this.Presets.Custom}>{ _t("Custom") }</option>
|
|
||||||
</select>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -1,106 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'RoomAlias',
|
|
||||||
propTypes: {
|
|
||||||
// Specifying a homeserver will make magical things happen when you,
|
|
||||||
// e.g. start typing in the room alias box.
|
|
||||||
homeserver: PropTypes.string,
|
|
||||||
alias: PropTypes.string,
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
onChange: function() {},
|
|
||||||
alias: '',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getAliasLocalpart: function() {
|
|
||||||
let room_alias = this.props.alias;
|
|
||||||
|
|
||||||
if (room_alias && this.props.homeserver) {
|
|
||||||
const suffix = ":" + this.props.homeserver;
|
|
||||||
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
|
|
||||||
room_alias = room_alias.slice(1, -suffix.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return room_alias;
|
|
||||||
},
|
|
||||||
|
|
||||||
onValueChanged: function(ev) {
|
|
||||||
this.props.onChange(ev.target.value);
|
|
||||||
},
|
|
||||||
|
|
||||||
onFocus: function(ev) {
|
|
||||||
const target = ev.target;
|
|
||||||
const curr_val = ev.target.value;
|
|
||||||
|
|
||||||
if (this.props.homeserver) {
|
|
||||||
if (curr_val == "") {
|
|
||||||
const self = this;
|
|
||||||
setTimeout(function() {
|
|
||||||
target.value = "#:" + self.props.homeserver;
|
|
||||||
target.setSelectionRange(1, 1);
|
|
||||||
}, 0);
|
|
||||||
} else {
|
|
||||||
const suffix = ":" + this.props.homeserver;
|
|
||||||
setTimeout(function() {
|
|
||||||
target.setSelectionRange(
|
|
||||||
curr_val.startsWith("#") ? 1 : 0,
|
|
||||||
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length,
|
|
||||||
);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onBlur: function(ev) {
|
|
||||||
const curr_val = ev.target.value;
|
|
||||||
|
|
||||||
if (this.props.homeserver) {
|
|
||||||
if (curr_val == "#:" + this.props.homeserver) {
|
|
||||||
ev.target.value = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (curr_val != "") {
|
|
||||||
let new_val = ev.target.value;
|
|
||||||
const suffix = ":" + this.props.homeserver;
|
|
||||||
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
|
|
||||||
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
|
|
||||||
ev.target.value = new_val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<input type="text" className="mx_RoomAlias" placeholder={_t("Address (optional)")}
|
|
||||||
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
|
|
||||||
value={this.props.alias} />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -21,9 +21,6 @@ import { IDialogProps } from "./IDialogProps";
|
||||||
import Field from "../elements/Field";
|
import Field from "../elements/Field";
|
||||||
import AccessibleButton from "../elements/AccessibleButton";
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import InfoTooltip from "../elements/InfoTooltip";
|
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
|
||||||
import {showCommunityRoomInviteDialog} from "../../../RoomInvite";
|
|
||||||
import { arrayFastClone } from "../../../utils/arrays";
|
import { arrayFastClone } from "../../../utils/arrays";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
@ -31,7 +28,6 @@ import InviteDialog from "./InviteDialog";
|
||||||
import BaseAvatar from "../avatars/BaseAvatar";
|
import BaseAvatar from "../avatars/BaseAvatar";
|
||||||
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
|
||||||
import {inviteMultipleToRoom, showAnyInviteErrors} from "../../../RoomInvite";
|
import {inviteMultipleToRoom, showAnyInviteErrors} from "../../../RoomInvite";
|
||||||
import {humanizeTime} from "../../../utils/humanize";
|
|
||||||
import StyledCheckbox from "../elements/StyledCheckbox";
|
import StyledCheckbox from "../elements/StyledCheckbox";
|
||||||
import Modal from "../../../Modal";
|
import Modal from "../../../Modal";
|
||||||
import ErrorDialog from "./ErrorDialog";
|
import ErrorDialog from "./ErrorDialog";
|
||||||
|
@ -171,7 +167,7 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
|
||||||
public render() {
|
public render() {
|
||||||
const emailAddresses = [];
|
const emailAddresses = [];
|
||||||
this.state.emailTargets.forEach((address, i) => {
|
this.state.emailTargets.forEach((address, i) => {
|
||||||
emailAddresses.push(
|
emailAddresses.push((
|
||||||
<Field
|
<Field
|
||||||
key={i}
|
key={i}
|
||||||
value={address}
|
value={address}
|
||||||
|
@ -180,11 +176,11 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
|
||||||
placeholder={_t("Email address")}
|
placeholder={_t("Email address")}
|
||||||
onBlur={() => this.onAddressBlur(i)}
|
onBlur={() => this.onAddressBlur(i)}
|
||||||
/>
|
/>
|
||||||
);
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Push a clean input
|
// Push a clean input
|
||||||
emailAddresses.push(
|
emailAddresses.push((
|
||||||
<Field
|
<Field
|
||||||
key={emailAddresses.length}
|
key={emailAddresses.length}
|
||||||
value={""}
|
value={""}
|
||||||
|
@ -192,23 +188,23 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
|
||||||
label={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
|
label={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
|
||||||
placeholder={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
|
placeholder={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
|
||||||
/>
|
/>
|
||||||
);
|
));
|
||||||
|
|
||||||
let peopleIntro = null;
|
let peopleIntro = null;
|
||||||
let people = [];
|
const people = [];
|
||||||
if (this.state.showPeople) {
|
if (this.state.showPeople) {
|
||||||
const humansToPresent = this.state.people.slice(0, this.state.numPeople);
|
const humansToPresent = this.state.people.slice(0, this.state.numPeople);
|
||||||
humansToPresent.forEach((person, i) => {
|
humansToPresent.forEach((person, i) => {
|
||||||
people.push(this.renderPerson(person, i));
|
people.push(this.renderPerson(person, i));
|
||||||
});
|
});
|
||||||
if (humansToPresent.length < this.state.people.length) {
|
if (humansToPresent.length < this.state.people.length) {
|
||||||
people.push(
|
people.push((
|
||||||
<AccessibleButton
|
<AccessibleButton
|
||||||
onClick={this.onShowMorePeople}
|
onClick={this.onShowMorePeople}
|
||||||
kind="link" key="more"
|
kind="link" key="more"
|
||||||
className="mx_CommunityPrototypeInviteDialog_morePeople"
|
className="mx_CommunityPrototypeInviteDialog_morePeople"
|
||||||
>{_t("Show more")}</AccessibleButton>
|
>{_t("Show more")}</AccessibleButton>
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (this.state.people.length > 0) {
|
if (this.state.people.length > 0) {
|
||||||
|
|
|
@ -163,8 +163,9 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
|
const classes = "mx_CreateCommunityPrototypeDialog_subtext mx_CreateCommunityPrototypeDialog_subtext_error";
|
||||||
helpText = (
|
helpText = (
|
||||||
<span className="mx_CreateCommunityPrototypeDialog_subtext mx_CreateCommunityPrototypeDialog_subtext_error">
|
<span className={classes}>
|
||||||
{this.state.error}
|
{this.state.error}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
@ -205,7 +206,10 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
|
||||||
ref={this.avatarUploadRef} accept="image/*"
|
ref={this.avatarUploadRef} accept="image/*"
|
||||||
onChange={this.onAvatarChanged}
|
onChange={this.onAvatarChanged}
|
||||||
/>
|
/>
|
||||||
<AccessibleButton onClick={this.onChangeAvatar} className="mx_CreateCommunityPrototypeDialog_avatarContainer">
|
<AccessibleButton
|
||||||
|
onClick={this.onChangeAvatar}
|
||||||
|
className="mx_CreateCommunityPrototypeDialog_avatarContainer"
|
||||||
|
>
|
||||||
{preview}
|
{preview}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
<div className="mx_CreateCommunityPrototypeDialog_tip">
|
<div className="mx_CreateCommunityPrototypeDialog_tip">
|
||||||
|
|
|
@ -186,8 +186,8 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
|
||||||
title = _t('Share Room Message');
|
title = _t('Share Room Message');
|
||||||
checkbox = <div>
|
checkbox = <div>
|
||||||
<StyledCheckbox
|
<StyledCheckbox
|
||||||
checked={this.state.linkSpecificEvent}
|
checked={this.state.linkSpecificEvent}
|
||||||
onClick={this.onLinkSpecificEventCheckboxClick}
|
onClick={this.onLinkSpecificEventCheckboxClick}
|
||||||
>
|
>
|
||||||
{ _t('Link to selected message') }
|
{ _t('Link to selected message') }
|
||||||
</StyledCheckbox>
|
</StyledCheckbox>
|
||||||
|
@ -198,16 +198,18 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
|
||||||
const encodedUrl = encodeURIComponent(matrixToUrl);
|
const encodedUrl = encodeURIComponent(matrixToUrl);
|
||||||
|
|
||||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||||
return <BaseDialog title={title}
|
return <BaseDialog
|
||||||
className='mx_ShareDialog'
|
title={title}
|
||||||
contentId='mx_Dialog_content'
|
className='mx_ShareDialog'
|
||||||
onFinished={this.props.onFinished}
|
contentId='mx_Dialog_content'
|
||||||
|
onFinished={this.props.onFinished}
|
||||||
>
|
>
|
||||||
<div className="mx_ShareDialog_content">
|
<div className="mx_ShareDialog_content">
|
||||||
<div className="mx_ShareDialog_matrixto">
|
<div className="mx_ShareDialog_matrixto">
|
||||||
<a href={matrixToUrl}
|
<a
|
||||||
onClick={ShareDialog.onLinkClick}
|
href={matrixToUrl}
|
||||||
className="mx_ShareDialog_matrixto_link"
|
onClick={ShareDialog.onLinkClick}
|
||||||
|
className="mx_ShareDialog_matrixto_link"
|
||||||
>
|
>
|
||||||
{ matrixToUrl }
|
{ matrixToUrl }
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -34,7 +34,6 @@ export interface ILocationState {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Draggable extends React.Component<IProps, IState> {
|
export default class Draggable extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -77,5 +76,4 @@ export default class Draggable extends React.Component<IProps, IState> {
|
||||||
render() {
|
render() {
|
||||||
return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />;
|
return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -39,11 +39,13 @@ interface IProps {
|
||||||
className: string;
|
className: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable camelcase */
|
||||||
interface IState {
|
interface IState {
|
||||||
userId: string;
|
userId: string;
|
||||||
displayname: string;
|
displayname: string;
|
||||||
avatar_url: string;
|
avatar_url: string;
|
||||||
}
|
}
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
const AVATAR_SIZE = 32;
|
const AVATAR_SIZE = 32;
|
||||||
|
|
||||||
|
@ -63,19 +65,18 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||||
const client = MatrixClientPeg.get();
|
const client = MatrixClientPeg.get();
|
||||||
const userId = client.getUserId();
|
const userId = client.getUserId();
|
||||||
const profileInfo = await client.getProfileInfo(userId);
|
const profileInfo = await client.getProfileInfo(userId);
|
||||||
const avatar_url = Avatar.avatarUrlForUser(
|
const avatarUrl = Avatar.avatarUrlForUser(
|
||||||
{avatarUrl: profileInfo.avatar_url},
|
{avatarUrl: profileInfo.avatar_url},
|
||||||
AVATAR_SIZE, AVATAR_SIZE, "crop");
|
AVATAR_SIZE, AVATAR_SIZE, "crop");
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
userId,
|
userId,
|
||||||
displayname: profileInfo.displayname,
|
displayname: profileInfo.displayname,
|
||||||
avatar_url,
|
avatar_url: avatarUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fakeEvent({userId, displayname, avatar_url}: IState) {
|
private fakeEvent({userId, displayname, avatar_url: avatarUrl}: IState) {
|
||||||
// Fake it till we make it
|
// Fake it till we make it
|
||||||
const event = new MatrixEvent(JSON.parse(`{
|
const event = new MatrixEvent(JSON.parse(`{
|
||||||
"type": "m.room.message",
|
"type": "m.room.message",
|
||||||
|
@ -85,12 +86,12 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||||
"msgtype": "m.text",
|
"msgtype": "m.text",
|
||||||
"body": "${this.props.message}",
|
"body": "${this.props.message}",
|
||||||
"displayname": "${displayname}",
|
"displayname": "${displayname}",
|
||||||
"avatar_url": "${avatar_url}"
|
"avatar_url": "${avatarUrl}"
|
||||||
},
|
},
|
||||||
"msgtype": "m.text",
|
"msgtype": "m.text",
|
||||||
"body": "${this.props.message}",
|
"body": "${this.props.message}",
|
||||||
"displayname": "${displayname}",
|
"displayname": "${displayname}",
|
||||||
"avatar_url": "${avatar_url}"
|
"avatar_url": "${avatarUrl}"
|
||||||
},
|
},
|
||||||
"unsigned": {
|
"unsigned": {
|
||||||
"age": 97
|
"age": 97
|
||||||
|
@ -104,7 +105,7 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||||
name: displayname,
|
name: displayname,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
getAvatarUrl: (..._) => {
|
getAvatarUrl: (..._) => {
|
||||||
return avatar_url;
|
return avatarUrl;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,13 +115,10 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
|
||||||
public render() {
|
public render() {
|
||||||
const event = this.fakeEvent(this.state);
|
const event = this.fakeEvent(this.state);
|
||||||
|
|
||||||
let className = classnames(
|
const className = classnames(this.props.className, {
|
||||||
this.props.className,
|
"mx_IRCLayout": this.props.useIRCLayout,
|
||||||
{
|
"mx_GroupLayout": !this.props.useIRCLayout,
|
||||||
"mx_IRCLayout": this.props.useIRCLayout,
|
});
|
||||||
"mx_GroupLayout": !this.props.useIRCLayout,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return <div className={className}>
|
return <div className={className}>
|
||||||
<EventTile mxEvent={event} useIRCLayout={this.props.useIRCLayout} />
|
<EventTile mxEvent={event} useIRCLayout={this.props.useIRCLayout} />
|
||||||
|
|
|
@ -198,11 +198,9 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {
|
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||||
element, prefixComponent, postfixComponent, className, onValidate, children,
|
const { element, prefixComponent, postfixComponent, className, onValidate, children,
|
||||||
tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props;
|
tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props;
|
||||||
|
|
||||||
// Set some defaults for the <input> element
|
// Set some defaults for the <input> element
|
||||||
|
|
|
@ -78,7 +78,12 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
|
||||||
|
|
||||||
private onMoueUp(event: MouseEvent) {
|
private onMoueUp(event: MouseEvent) {
|
||||||
if (this.props.roomId) {
|
if (this.props.roomId) {
|
||||||
SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width);
|
SettingsStore.setValue(
|
||||||
|
"ircDisplayNameWidth",
|
||||||
|
this.props.roomId,
|
||||||
|
SettingLevel.ROOM_DEVICE,
|
||||||
|
this.state.width,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ limitations under the License.
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import AccessibleButton from "./AccessibleButton";
|
|
||||||
import Tooltip from './Tooltip';
|
import Tooltip from './Tooltip';
|
||||||
import { _t } from "../../../languageHandler";
|
import { _t } from "../../../languageHandler";
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ const QRCode: React.FC<IProps> = ({data, className, ...options}) => {
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
}, [JSON.stringify(data), options]);
|
}, [JSON.stringify(data), options]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
return <div className={classNames("mx_QRCode", className)}>
|
return <div className={classNames("mx_QRCode", className)}>
|
||||||
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }
|
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }
|
||||||
|
|
|
@ -45,7 +45,7 @@ export default class Slider extends React.Component<IProps> {
|
||||||
// non linear slider.
|
// non linear slider.
|
||||||
private offset(values: number[], value: number): number {
|
private offset(values: number[], value: number): number {
|
||||||
// the index of the first number greater than value.
|
// the index of the first number greater than value.
|
||||||
let closest = values.reduce((prev, curr) => {
|
const closest = values.reduce((prev, curr) => {
|
||||||
return (value > curr ? prev + 1 : prev);
|
return (value > curr ? prev + 1 : prev);
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
||||||
|
@ -68,17 +68,16 @@ export default class Slider extends React.Component<IProps> {
|
||||||
const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue);
|
const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue);
|
||||||
|
|
||||||
return 100 * (closest - 1 + linearInterpolation) * intervalWidth;
|
return 100 * (closest - 1 + linearInterpolation) * intervalWidth;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
render(): React.ReactNode {
|
||||||
const dots = this.props.values.map(v =>
|
const dots = this.props.values.map(v => <Dot
|
||||||
<Dot active={v <= this.props.value}
|
active={v <= this.props.value}
|
||||||
label={this.props.displayFunc(v)}
|
label={this.props.displayFunc(v)}
|
||||||
onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)}
|
onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)}
|
||||||
key={v}
|
key={v}
|
||||||
disabled={this.props.disabled}
|
disabled={this.props.disabled}
|
||||||
/>);
|
/>);
|
||||||
|
|
||||||
let selection = null;
|
let selection = null;
|
||||||
|
|
||||||
|
@ -93,7 +92,7 @@ export default class Slider extends React.Component<IProps> {
|
||||||
return <div className="mx_Slider">
|
return <div className="mx_Slider">
|
||||||
<div>
|
<div>
|
||||||
<div className="mx_Slider_bar">
|
<div className="mx_Slider_bar">
|
||||||
<hr onClick={this.props.disabled ? () => {} : this.onClick.bind(this)}/>
|
<hr onClick={this.props.disabled ? () => {} : this.onClick.bind(this)} />
|
||||||
{ selection }
|
{ selection }
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_Slider_dotContainer">
|
<div className="mx_Slider_dotContainer">
|
||||||
|
|
|
@ -17,8 +17,6 @@ limitations under the License.
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||||
|
|
||||||
const CHECK_BOX_SVG = require("../../../../res/img/feather-customised/check.svg");
|
|
||||||
|
|
||||||
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,13 +37,14 @@ export default class StyledCheckbox extends React.PureComponent<IProps, IState>
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
|
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||||
const { children, className, ...otherProps } = this.props;
|
const { children, className, ...otherProps } = this.props;
|
||||||
return <span className={"mx_Checkbox " + className}>
|
return <span className={"mx_Checkbox " + className}>
|
||||||
<input id={this.id} {...otherProps} type="checkbox" />
|
<input id={this.id} {...otherProps} type="checkbox" />
|
||||||
<label htmlFor={this.id}>
|
<label htmlFor={this.id}>
|
||||||
{/* Using the div to center the image */}
|
{/* Using the div to center the image */}
|
||||||
<div className="mx_Checkbox_background">
|
<div className="mx_Checkbox_background">
|
||||||
<img src={CHECK_BOX_SVG}/>
|
<img src={require("../../../../res/img/feather-customised/check.svg")} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{ this.props.children }
|
{ this.props.children }
|
||||||
|
@ -53,4 +52,4 @@ export default class StyledCheckbox extends React.PureComponent<IProps, IState>
|
||||||
</label>
|
</label>
|
||||||
</span>;
|
</span>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'UserSelector',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
onChange: PropTypes.func,
|
|
||||||
selected_users: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
onChange: function() {},
|
|
||||||
selected: [],
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
this._user_id_input = createRef();
|
|
||||||
},
|
|
||||||
|
|
||||||
addUser: function(user_id) {
|
|
||||||
if (this.props.selected_users.indexOf(user_id == -1)) {
|
|
||||||
this.props.onChange(this.props.selected_users.concat([user_id]));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
removeUser: function(user_id) {
|
|
||||||
this.props.onChange(this.props.selected_users.filter(function(e) {
|
|
||||||
return e != user_id;
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
onAddUserId: function() {
|
|
||||||
this.addUser(this._user_id_input.current.value);
|
|
||||||
this._user_id_input.current.value = "";
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const self = this;
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<ul className="mx_UserSelector_UserIdList">
|
|
||||||
{ this.props.selected_users.map(function(user_id, i) {
|
|
||||||
return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
|
|
||||||
}) }
|
|
||||||
</ul>
|
|
||||||
<input type="text" ref={this._user_id_input} defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} />
|
|
||||||
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">
|
|
||||||
{ _t("Add User") }
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from "react";
|
import React, {forwardRef} from "react";
|
||||||
|
|
||||||
export default ({mxEvent}) => {
|
export default forwardRef(({mxEvent}, ref) => {
|
||||||
const text = mxEvent.getContent().body;
|
const text = mxEvent.getContent().body;
|
||||||
return (
|
return (
|
||||||
<span className="mx_UnknownBody">
|
<span className="mx_UnknownBody" ref={ref}>
|
||||||
{ text }
|
{ text }
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
|
@ -76,14 +76,16 @@ const EncryptionInfo: React.FC<IProps> = ({
|
||||||
description = (
|
description = (
|
||||||
<div>
|
<div>
|
||||||
<p>{_t("Messages in this room are end-to-end encrypted.")}</p>
|
<p>{_t("Messages in this room are end-to-end encrypted.")}</p>
|
||||||
<p>{_t("Your messages are secured and only you and the recipient have the unique keys to unlock them.")}</p>
|
<p>{_t("Your messages are secured and only you and the recipient have " +
|
||||||
|
"the unique keys to unlock them.")}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
description = (
|
description = (
|
||||||
<div>
|
<div>
|
||||||
<p>{_t("Messages in this room are not end-to-end encrypted.")}</p>
|
<p>{_t("Messages in this room are not end-to-end encrypted.")}</p>
|
||||||
<p>{_t("In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.")}</p>
|
<p>{_t("In encrypted rooms, your messages are secured and only you and the recipient have " +
|
||||||
|
"the unique keys to unlock them.")}</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,10 @@ import dis from '../../../dispatcher/dispatcher';
|
||||||
import RightPanelStore from "../../../stores/RightPanelStore";
|
import RightPanelStore from "../../../stores/RightPanelStore";
|
||||||
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
|
||||||
import {Action} from '../../../dispatcher/actions';
|
import {Action} from '../../../dispatcher/actions';
|
||||||
import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
|
import {
|
||||||
|
SetRightPanelPhasePayload,
|
||||||
|
SetRightPanelPhaseRefireParams,
|
||||||
|
} from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
|
||||||
import {EventSubscription} from "fbemitter";
|
import {EventSubscription} from "fbemitter";
|
||||||
|
|
||||||
export enum HeaderKind {
|
export enum HeaderKind {
|
||||||
|
@ -38,7 +41,7 @@ interface IState {
|
||||||
|
|
||||||
interface IProps {}
|
interface IProps {}
|
||||||
|
|
||||||
export default class HeaderButtons extends React.Component<IProps, IState> {
|
export default abstract class HeaderButtons extends React.Component<IProps, IState> {
|
||||||
private storeToken: EventSubscription;
|
private storeToken: EventSubscription;
|
||||||
private dispatcherRef: string;
|
private dispatcherRef: string;
|
||||||
|
|
||||||
|
@ -92,14 +95,7 @@ export default class HeaderButtons extends React.Component<IProps, IState> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// XXX: Make renderButtons a prop
|
// XXX: Make renderButtons a prop
|
||||||
public renderButtons(): JSX.Element[] {
|
public abstract renderButtons(): JSX.Element[];
|
||||||
// Ignore - intended to be overridden by subclasses
|
|
||||||
// Return empty fragment to satisfy the type
|
|
||||||
return [
|
|
||||||
<React.Fragment>
|
|
||||||
</React.Fragment>
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
// inline style as this will be swapped around in future commits
|
// inline style as this will be swapped around in future commits
|
||||||
|
|
|
@ -30,8 +30,6 @@ import {_t} from "../../../languageHandler";
|
||||||
import SdkConfig from "../../../SdkConfig";
|
import SdkConfig from "../../../SdkConfig";
|
||||||
import E2EIcon from "../rooms/E2EIcon";
|
import E2EIcon from "../rooms/E2EIcon";
|
||||||
import {
|
import {
|
||||||
PHASE_UNSENT,
|
|
||||||
PHASE_REQUESTED,
|
|
||||||
PHASE_READY,
|
PHASE_READY,
|
||||||
PHASE_DONE,
|
PHASE_DONE,
|
||||||
PHASE_STARTED,
|
PHASE_STARTED,
|
||||||
|
@ -104,14 +102,15 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
if (showSAS) {
|
if (showSAS) {
|
||||||
sasBlockDialog =
|
sasBlockDialog = <div className='mx_VerificationPanel_QRPhase_startOption'>
|
||||||
<div className='mx_VerificationPanel_QRPhase_startOption'>
|
<p>{_t("Compare unique emoji")}</p>
|
||||||
<p>{_t("Compare unique emoji")}</p>
|
<span className='mx_VerificationPanel_QRPhase_helpText'>
|
||||||
<span className='mx_VerificationPanel_QRPhase_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
|
{_t("Compare a unique set of emoji if you don't have a camera on either device")}
|
||||||
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this.startSAS} kind='primary'>
|
</span>
|
||||||
{_t("Start")}
|
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this.startSAS} kind='primary'>
|
||||||
</AccessibleButton>
|
{_t("Start")}
|
||||||
</div>;
|
</AccessibleButton>
|
||||||
|
</div>;
|
||||||
}
|
}
|
||||||
const or = qrBlockDialog && sasBlockDialog ?
|
const or = qrBlockDialog && sasBlockDialog ?
|
||||||
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
|
<div className='mx_VerificationPanel_QRPhase_betweenText'>{_t("or")}</div> : null;
|
||||||
|
@ -165,8 +164,8 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
|
||||||
}
|
}
|
||||||
|
|
||||||
const noCommonMethodBlock = noCommonMethodError ?
|
const noCommonMethodBlock = noCommonMethodError ?
|
||||||
<div className="mx_UserInfo_container">{noCommonMethodError}</div> :
|
<div className="mx_UserInfo_container">{noCommonMethodError}</div> :
|
||||||
null;
|
null;
|
||||||
|
|
||||||
// TODO: add way to open camera to scan a QR code
|
// TODO: add way to open camera to scan a QR code
|
||||||
return <React.Fragment>
|
return <React.Fragment>
|
||||||
|
|
|
@ -92,6 +92,7 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
|
||||||
};
|
};
|
||||||
|
|
||||||
public render(): React.ReactElement {
|
public render(): React.ReactElement {
|
||||||
|
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
|
||||||
const {notification, forceCount, roomId, onClick, ...props} = this.props;
|
const {notification, forceCount, roomId, onClick, ...props} = this.props;
|
||||||
|
|
||||||
// Don't show a badge if we don't need to
|
// Don't show a badge if we don't need to
|
||||||
|
|
|
@ -218,7 +218,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
|
|
||||||
private getRoomDelta = (roomId: string, delta: number, unread = false) => {
|
private getRoomDelta = (roomId: string, delta: number, unread = false) => {
|
||||||
const lists = RoomListStore.instance.orderedLists;
|
const lists = RoomListStore.instance.orderedLists;
|
||||||
let rooms: Room = [];
|
const rooms: Room = [];
|
||||||
TAG_ORDER.forEach(t => {
|
TAG_ORDER.forEach(t => {
|
||||||
let listRooms = lists[t];
|
let listRooms = lists[t];
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
// TODO: Put community invites in a more sensible place (not in the room list)
|
// TODO: Put community invites in a more sensible place (not in the room list)
|
||||||
// See https://github.com/vector-im/element-web/issues/14456
|
// See https://github.com/vector-im/element-web/issues/14456
|
||||||
return MatrixClientPeg.get().getGroups().filter(g => {
|
return MatrixClientPeg.get().getGroups().filter(g => {
|
||||||
return g.myMembership === 'invite';
|
return g.myMembership === 'invite';
|
||||||
}).map(g => {
|
}).map(g => {
|
||||||
const avatar = (
|
const avatar = (
|
||||||
<GroupAvatar
|
<GroupAvatar
|
||||||
|
@ -346,21 +346,19 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
|
||||||
: TAG_AESTHETICS[orderedTagId];
|
: TAG_AESTHETICS[orderedTagId];
|
||||||
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
|
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
|
||||||
|
|
||||||
components.push(
|
components.push(<RoomSublist
|
||||||
<RoomSublist
|
key={`sublist-${orderedTagId}`}
|
||||||
key={`sublist-${orderedTagId}`}
|
tagId={orderedTagId}
|
||||||
tagId={orderedTagId}
|
forRooms={true}
|
||||||
forRooms={true}
|
startAsHidden={aesthetics.defaultHidden}
|
||||||
startAsHidden={aesthetics.defaultHidden}
|
label={aesthetics.sectionLabelRaw ? aesthetics.sectionLabelRaw : _t(aesthetics.sectionLabel)}
|
||||||
label={aesthetics.sectionLabelRaw ? aesthetics.sectionLabelRaw : _t(aesthetics.sectionLabel)}
|
onAddRoom={aesthetics.onAddRoom}
|
||||||
onAddRoom={aesthetics.onAddRoom}
|
addRoomLabel={aesthetics.addRoomLabel ? _t(aesthetics.addRoomLabel) : aesthetics.addRoomLabel}
|
||||||
addRoomLabel={aesthetics.addRoomLabel ? _t(aesthetics.addRoomLabel) : aesthetics.addRoomLabel}
|
addRoomContextMenu={aesthetics.addRoomContextMenu}
|
||||||
addRoomContextMenu={aesthetics.addRoomContextMenu}
|
isMinimized={this.props.isMinimized}
|
||||||
isMinimized={this.props.isMinimized}
|
onResize={this.props.onResize}
|
||||||
onResize={this.props.onResize}
|
extraBadTilesThatShouldntExist={extraTiles}
|
||||||
extraBadTilesThatShouldntExist={extraTiles}
|
/>);
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return components;
|
return components;
|
||||||
|
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
|
||||||
import * as sdk from "../../../index";
|
|
||||||
import { _t } from '../../../languageHandler';
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'RoomNameEditor',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object.isRequired,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
name: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
|
||||||
UNSAFE_componentWillMount: function() {
|
|
||||||
const room = this.props.room;
|
|
||||||
const name = room.currentState.getStateEvents('m.room.name', '');
|
|
||||||
const myId = MatrixClientPeg.get().credentials.userId;
|
|
||||||
const defaultName = room.getDefaultRoomName(myId);
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
name: name ? name.getContent().name : '',
|
|
||||||
});
|
|
||||||
|
|
||||||
this._placeholderName = _t("Unnamed Room");
|
|
||||||
if (defaultName && defaultName !== 'Empty room') { // default name from JS SDK, needs no translation as we don't ever show it.
|
|
||||||
this._placeholderName += " (" + defaultName + ")";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getRoomName: function() {
|
|
||||||
return this.state.name;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onValueChanged: function(value, shouldSubmit) {
|
|
||||||
this.setState({
|
|
||||||
name: value,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const EditableText = sdk.getComponent("elements.EditableText");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="mx_RoomHeader_name">
|
|
||||||
<EditableText
|
|
||||||
className="mx_RoomHeader_nametext mx_RoomHeader_editable"
|
|
||||||
placeholderClassName="mx_RoomHeader_placeholder"
|
|
||||||
placeholder={this._placeholderName}
|
|
||||||
blurToCancel={false}
|
|
||||||
initialValue={this.state.name}
|
|
||||||
onValueChanged={this._onValueChanged}
|
|
||||||
dir="auto" />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -517,15 +517,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
if (this.state.rooms) {
|
if (this.state.rooms) {
|
||||||
const visibleRooms = this.state.rooms.slice(0, this.numVisibleTiles);
|
const visibleRooms = this.state.rooms.slice(0, this.numVisibleTiles);
|
||||||
for (const room of visibleRooms) {
|
for (const room of visibleRooms) {
|
||||||
tiles.push(
|
tiles.push(<RoomTile
|
||||||
<RoomTile
|
room={room}
|
||||||
room={room}
|
key={`room-${room.roomId}`}
|
||||||
key={`room-${room.roomId}`}
|
showMessagePreview={this.layout.showPreviews}
|
||||||
showMessagePreview={this.layout.showPreviews}
|
isMinimized={this.props.isMinimized}
|
||||||
isMinimized={this.props.isMinimized}
|
tag={this.props.tagId}
|
||||||
tag={this.props.tagId}
|
/>);
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,7 +708,12 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
// doesn't become sticky.
|
// doesn't become sticky.
|
||||||
// The same applies to the notification badge.
|
// The same applies to the notification badge.
|
||||||
return (
|
return (
|
||||||
<div className={classes} onKeyDown={this.onHeaderKeyDown} onFocus={onFocus} aria-label={this.props.label}>
|
<div
|
||||||
|
className={classes}
|
||||||
|
onKeyDown={this.onHeaderKeyDown}
|
||||||
|
onFocus={onFocus}
|
||||||
|
aria-label={this.props.label}
|
||||||
|
>
|
||||||
<div className="mx_RoomSublist_stickable">
|
<div className="mx_RoomSublist_stickable">
|
||||||
<Button
|
<Button
|
||||||
onFocus={onFocus}
|
onFocus={onFocus}
|
||||||
|
@ -762,7 +765,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
|
||||||
const showMoreAtMinHeight = minTiles < this.numTiles;
|
const showMoreAtMinHeight = minTiles < this.numTiles;
|
||||||
const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0);
|
const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0);
|
||||||
const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding);
|
const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding);
|
||||||
let maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
|
const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
|
||||||
const showMoreBtnClasses = classNames({
|
const showMoreBtnClasses = classNames({
|
||||||
'mx_RoomSublist_showNButton': true,
|
'mx_RoomSublist_showNButton': true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,7 +31,7 @@ import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextM
|
||||||
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
|
||||||
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
|
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
|
||||||
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
|
||||||
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE, } from "../../../RoomNotifs";
|
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
|
||||||
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
||||||
import NotificationBadge from "./NotificationBadge";
|
import NotificationBadge from "./NotificationBadge";
|
||||||
import { Volume } from "../../../RoomNotifsTypes";
|
import { Volume } from "../../../RoomNotifsTypes";
|
||||||
|
@ -48,7 +48,7 @@ import IconizedContextMenu, {
|
||||||
IconizedContextMenuCheckbox,
|
IconizedContextMenuCheckbox,
|
||||||
IconizedContextMenuOption,
|
IconizedContextMenuOption,
|
||||||
IconizedContextMenuOptionList,
|
IconizedContextMenuOptionList,
|
||||||
IconizedContextMenuRadio
|
IconizedContextMenuRadio,
|
||||||
} from "../context_menus/IconizedContextMenu";
|
} from "../context_menus/IconizedContextMenu";
|
||||||
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
|
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
|
||||||
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
|
||||||
|
@ -249,7 +249,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
|
||||||
removeTag,
|
removeTag,
|
||||||
addTag,
|
addTag,
|
||||||
undefined,
|
undefined,
|
||||||
0
|
0,
|
||||||
));
|
));
|
||||||
} else {
|
} else {
|
||||||
console.warn(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`);
|
console.warn(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`);
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import createReactClass from 'create-react-class';
|
|
||||||
import * as sdk from '../../../index';
|
|
||||||
import { _t } from "../../../languageHandler";
|
|
||||||
|
|
||||||
export default createReactClass({
|
|
||||||
displayName: 'RoomTopicEditor',
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
room: PropTypes.object.isRequired,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
topic: null,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
const room = this.props.room;
|
|
||||||
const topic = room.currentState.getStateEvents('m.room.topic', '');
|
|
||||||
this.setState({
|
|
||||||
topic: topic ? topic.getContent().topic : '',
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getTopic: function() {
|
|
||||||
return this.state.topic;
|
|
||||||
},
|
|
||||||
|
|
||||||
_onValueChanged: function(value) {
|
|
||||||
this.setState({
|
|
||||||
topic: value,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const EditableText = sdk.getComponent("elements.EditableText");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<EditableText
|
|
||||||
className="mx_RoomHeader_topic mx_RoomHeader_editable"
|
|
||||||
placeholderClassName="mx_RoomHeader_placeholder"
|
|
||||||
placeholder={_t("Add a topic")}
|
|
||||||
blurToCancel={false}
|
|
||||||
initialValue={this.state.topic}
|
|
||||||
onValueChanged={this._onValueChanged}
|
|
||||||
dir="auto" />
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -19,9 +19,7 @@ import classNames from "classnames";
|
||||||
import {
|
import {
|
||||||
RovingAccessibleButton,
|
RovingAccessibleButton,
|
||||||
RovingAccessibleTooltipButton,
|
RovingAccessibleTooltipButton,
|
||||||
RovingTabIndexWrapper
|
|
||||||
} from "../../../accessibility/RovingTabIndex";
|
} from "../../../accessibility/RovingTabIndex";
|
||||||
import AccessibleButton from "../../views/elements/AccessibleButton";
|
|
||||||
import NotificationBadge from "./NotificationBadge";
|
import NotificationBadge from "./NotificationBadge";
|
||||||
import { NotificationState } from "../../../stores/notifications/NotificationState";
|
import { NotificationState } from "../../../stores/notifications/NotificationState";
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ function getStatusText(status: UpdateCheckStatus, errorDetail?: string) {
|
||||||
return _t('Downloading update...');
|
return _t('Downloading update...');
|
||||||
case UpdateCheckStatus.Ready:
|
case UpdateCheckStatus.Ready:
|
||||||
return _t("New version available. <a>Update now.</a>", {}, {
|
return _t("New version available. <a>Update now.</a>", {}, {
|
||||||
a: sub => <AccessibleButton kind="link" onClick={installUpdate}>{sub}</AccessibleButton>
|
a: sub => <AccessibleButton kind="link" onClick={installUpdate}>{sub}</AccessibleButton>,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,7 +170,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
||||||
"baseFontSize",
|
"baseFontSize",
|
||||||
null,
|
null,
|
||||||
SettingLevel.DEVICE,
|
SettingLevel.DEVICE,
|
||||||
parseInt(value, 10) - FontWatcher.SIZE_DIFF
|
parseInt(value, 10) - FontWatcher.SIZE_DIFF,
|
||||||
);
|
);
|
||||||
|
|
||||||
return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})};
|
return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})};
|
||||||
|
@ -294,7 +294,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{customThemeForm}
|
{customThemeForm}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,15 @@ interface IProps extends IGenericToastProps {
|
||||||
|
|
||||||
const SECOND = 1000;
|
const SECOND = 1000;
|
||||||
|
|
||||||
const GenericExpiringToast: React.FC<IProps> = ({description, acceptLabel, dismissLabel, onAccept, onDismiss, toastKey, numSeconds}) => {
|
const GenericExpiringToast: React.FC<IProps> = ({
|
||||||
|
description,
|
||||||
|
acceptLabel,
|
||||||
|
dismissLabel,
|
||||||
|
onAccept,
|
||||||
|
onDismiss,
|
||||||
|
toastKey,
|
||||||
|
numSeconds,
|
||||||
|
}) => {
|
||||||
const onReject = () => {
|
const onReject = () => {
|
||||||
if (onDismiss) onDismiss();
|
if (onDismiss) onDismiss();
|
||||||
ToastStore.sharedInstance().dismissToast(toastKey);
|
ToastStore.sharedInstance().dismissToast(toastKey);
|
||||||
|
|
|
@ -31,7 +31,13 @@ interface IPropsExtended extends IProps {
|
||||||
onReject();
|
onReject();
|
||||||
}
|
}
|
||||||
|
|
||||||
const GenericToast: React.FC<XOR<IPropsExtended, IProps>> = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => {
|
const GenericToast: React.FC<XOR<IPropsExtended, IProps>> = ({
|
||||||
|
description,
|
||||||
|
acceptLabel,
|
||||||
|
rejectLabel,
|
||||||
|
onAccept,
|
||||||
|
onReject,
|
||||||
|
}) => {
|
||||||
return <div>
|
return <div>
|
||||||
<div className="mx_Toast_description">
|
<div className="mx_Toast_description">
|
||||||
{ description }
|
{ description }
|
||||||
|
|
|
@ -97,10 +97,7 @@ export default class CallView extends React.Component<IProps, IState> {
|
||||||
if (this.props.room) {
|
if (this.props.room) {
|
||||||
const roomId = this.props.room.roomId;
|
const roomId = this.props.room.roomId;
|
||||||
call = CallHandler.getCallForRoom(roomId) ||
|
call = CallHandler.getCallForRoom(roomId) ||
|
||||||
(this.props.ConferenceHandler ?
|
(this.props.ConferenceHandler ? this.props.ConferenceHandler.getConferenceCallForRoom(roomId) : null);
|
||||||
this.props.ConferenceHandler.getConferenceCallForRoom(roomId) :
|
|
||||||
null
|
|
||||||
);
|
|
||||||
|
|
||||||
if (this.call) {
|
if (this.call) {
|
||||||
this.setState({ call: call });
|
this.setState({ call: call });
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
|
||||||
|
|
||||||
private onAction = (payload: ActionPayload) => {
|
private onAction = (payload: ActionPayload) => {
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'call_state':
|
case 'call_state': {
|
||||||
const call = CallHandler.getCall(payload.room_id);
|
const call = CallHandler.getCall(payload.room_id);
|
||||||
if (call && call.call_state === 'ringing') {
|
if (call && call.call_state === 'ringing') {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -62,6 +62,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
|
||||||
incomingCall: null,
|
incomingCall: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1200,7 +1200,6 @@
|
||||||
"%(count)s unread messages.|other": "%(count)s unread messages.",
|
"%(count)s unread messages.|other": "%(count)s unread messages.",
|
||||||
"%(count)s unread messages.|one": "1 unread message.",
|
"%(count)s unread messages.|one": "1 unread message.",
|
||||||
"Unread messages.": "Unread messages.",
|
"Unread messages.": "Unread messages.",
|
||||||
"Add a topic": "Add a topic",
|
|
||||||
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
|
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
|
||||||
"This room has already been upgraded.": "This room has already been upgraded.",
|
"This room has already been upgraded.": "This room has already been upgraded.",
|
||||||
"This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.",
|
"This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.",
|
||||||
|
@ -1556,8 +1555,6 @@
|
||||||
"Room directory": "Room directory",
|
"Room directory": "Room directory",
|
||||||
"Sign in with single sign-on": "Sign in with single sign-on",
|
"Sign in with single sign-on": "Sign in with single sign-on",
|
||||||
"And %(count)s more...|other": "And %(count)s more...",
|
"And %(count)s more...|other": "And %(count)s more...",
|
||||||
"ex. @bob:example.com": "ex. @bob:example.com",
|
|
||||||
"Add User": "Add User",
|
|
||||||
"Home": "Home",
|
"Home": "Home",
|
||||||
"Enter a server name": "Enter a server name",
|
"Enter a server name": "Enter a server name",
|
||||||
"Looks good": "Looks good",
|
"Looks good": "Looks good",
|
||||||
|
@ -1888,10 +1885,6 @@
|
||||||
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
|
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
|
||||||
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
|
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
|
||||||
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>",
|
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>",
|
||||||
"Private Chat": "Private Chat",
|
|
||||||
"Public Chat": "Public Chat",
|
|
||||||
"Custom": "Custom",
|
|
||||||
"Address (optional)": "Address (optional)",
|
|
||||||
"Reject invitation": "Reject invitation",
|
"Reject invitation": "Reject invitation",
|
||||||
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
|
||||||
"Unable to reject invite": "Unable to reject invite",
|
"Unable to reject invite": "Unable to reject invite",
|
||||||
|
@ -1985,11 +1978,6 @@
|
||||||
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
|
||||||
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
|
||||||
"Sign in with SSO": "Sign in with SSO",
|
"Sign in with SSO": "Sign in with SSO",
|
||||||
"Sorry, your browser is <b>not</b> able to run %(brand)s.": "Sorry, your browser is <b>not</b> able to run %(brand)s.",
|
|
||||||
"%(brand)s uses many advanced browser features, some of which are not available or experimental in your current browser.": "%(brand)s uses many advanced browser features, some of which are not available or experimental in your current browser.",
|
|
||||||
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
|
|
||||||
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!",
|
|
||||||
"I understand the risks and wish to continue": "I understand the risks and wish to continue",
|
|
||||||
"Couldn't load page": "Couldn't load page",
|
"Couldn't load page": "Couldn't load page",
|
||||||
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
|
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
|
||||||
"You must join the room to see its files": "You must join the room to see its files",
|
"You must join the room to see its files": "You must join the room to see its files",
|
||||||
|
|
|
@ -442,7 +442,7 @@ export function pickBestLanguage(langs: string[]): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLangsJson(): Promise<object> {
|
function getLangsJson(): Promise<object> {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let url;
|
let url;
|
||||||
if (typeof(webpackLangJsonUrl) === 'string') { // in Jest this 'url' isn't a URL, so just fall through
|
if (typeof(webpackLangJsonUrl) === 'string') { // in Jest this 'url' isn't a URL, so just fall through
|
||||||
url = webpackLangJsonUrl;
|
url = webpackLangJsonUrl;
|
||||||
|
@ -453,7 +453,7 @@ function getLangsJson(): Promise<object> {
|
||||||
{ method: "GET", url },
|
{ method: "GET", url },
|
||||||
(err, response, body) => {
|
(err, response, body) => {
|
||||||
if (err || response.status < 200 || response.status >= 300) {
|
if (err || response.status < 200 || response.status >= 300) {
|
||||||
reject({err: err, response: response});
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(JSON.parse(body));
|
resolve(JSON.parse(body));
|
||||||
|
@ -488,7 +488,7 @@ function getLanguage(langPath: string): object {
|
||||||
{ method: "GET", url: langPath },
|
{ method: "GET", url: langPath },
|
||||||
(err, response, body) => {
|
(err, response, body) => {
|
||||||
if (err || response.status < 200 || response.status >= 300) {
|
if (err || response.status < 200 || response.status >= 300) {
|
||||||
reject({err: err, response: response});
|
reject(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resolve(weblateToCounterpart(JSON.parse(body)));
|
resolve(weblateToCounterpart(JSON.parse(body)));
|
||||||
|
|
Loading…
Reference in a new issue