Merge branch 'develop' into gsouquet/dialogs-ts-migration

This commit is contained in:
Germain Souquet 2021-06-18 12:27:00 +01:00
commit 3f1ca970d6
80 changed files with 1061 additions and 449 deletions

View file

@ -1,4 +1,4 @@
name: Develop jobs
name: Develop
on:
push:
branches: [develop]
@ -20,8 +20,19 @@ jobs:
test/end-to-end-tests/logs/**/*
test/end-to-end-tests/synapse/installations/consent/homeserver.log
retention-days: 14
- name: Archive performance benchmark
uses: actions/upload-artifact@v2
- name: Download previous benchmark data
uses: actions/cache@v1
with:
name: performance-entries.json
path: test/end-to-end-tests/performance-entries.json
path: ./cache
key: ${{ runner.os }}-benchmark
- name: Store benchmark result
uses: matrix-org/github-action-benchmark@jsperfentry-1
with:
tool: 'jsperformanceentry'
output-file-path: test/end-to-end-tests/performance-entries.json
fail-on-alert: false
comment-on-alert: false
# Only temporary to monitor where failures occur
alert-comment-cc-users: '@gsouquet'
github-token: ${{ secrets.DEPLOY_GH_PAGES }}
auto-push: ${{ github.ref == 'refs/heads/develop' }}

View file

@ -157,7 +157,7 @@
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-fetch-mock": "^3.0.3",
"matrix-mock-request": "^1.2.3",
"matrix-react-test-utils": "^0.2.2",
"matrix-react-test-utils": "^0.2.3",
"matrix-web-i18n": "github:matrix-org/matrix-web-i18n",
"react-test-renderer": "^17.0.2",
"rimraf": "^3.0.2",

View file

@ -178,7 +178,7 @@ $irc-line-height: $font-18px;
overflow: hidden;
display: flex;
> .mx_SenderProfile_name {
> .mx_SenderProfile_displayName {
overflow: hidden;
text-overflow: ellipsis;
min-width: var(--name-width);
@ -207,7 +207,7 @@ $irc-line-height: $font-18px;
background: transparent;
> span {
> .mx_SenderProfile_name {
> .mx_SenderProfile_displayName {
min-width: inherit;
}
}

View file

@ -98,5 +98,29 @@ limitations under the License.
line-height: $font-24px;
}
}
.mx_IncomingCallBox_iconButton {
position: absolute;
right: 8px;
&::before {
content: '';
height: 20px;
width: 20px;
background-color: $icon-button-color;
mask-repeat: no-repeat;
mask-size: contain;
mask-position: center;
}
}
.mx_IncomingCallBox_silence::before {
mask-image: url('$(res)/img/voip/silence.svg');
}
.mx_IncomingCallBox_unSilence::before {
mask-image: url('$(res)/img/voip/un-silence.svg');
}
}
}

3
res/img/voip/silence.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.56986 1.82566L4 4.80054L1.5 4.80054C0.671573 4.80054 0 5.47212 0 6.30054V9.70054C0 10.529 0.671573 11.2005 1.5 11.2005L4 11.2005L7.56986 14.1754C8.05836 14.5825 8.8 14.2351 8.8 13.5993L8.8 9.70054V6.30054L8.8 2.40182C8.8 1.76595 8.05836 1.41858 7.56986 1.82566ZM14.1546 2.76877C13.9162 2.46224 13.4745 2.40702 13.1679 2.64543L13.0443 3.6318L13.0446 3.63212L13.0448 3.63238L13.0536 3.64417C13.0623 3.65582 13.0764 3.67498 13.0951 3.7013C13.1325 3.75399 13.1883 3.83518 13.2564 3.94222C13.3929 4.15668 13.5774 4.47271 13.7624 4.86922C14.1345 5.66647 14.4965 6.763 14.4965 8.00044C14.4965 9.23789 14.1345 10.3344 13.7624 11.1317C13.5774 11.5282 13.3929 11.8442 13.2564 12.0587C13.1883 12.1657 13.1325 12.2469 13.0951 12.2996C13.0764 12.3259 13.0623 12.3451 13.0536 12.3567L13.0448 12.3685L13.0446 12.3688L13.0443 12.3691L13.0441 12.3694L13.0438 12.3698L13.0436 12.37C12.8063 12.6765 12.8618 13.1174 13.1679 13.3555C13.4745 13.5939 13.9162 13.5386 14.1546 13.2321L13.5996 12.8004C14.1546 13.2321 14.1548 13.2319 14.1549 13.2317L14.1552 13.2313L14.156 13.2303L14.158 13.2278L14.1636 13.2204L14.1815 13.1966C14.1963 13.1768 14.2166 13.1491 14.2416 13.1138C14.2917 13.0433 14.3609 12.9423 14.4428 12.8136C14.6063 12.5567 14.8218 12.187 15.0368 11.7264C15.4647 10.8093 15.9027 9.50586 15.9027 8.00044C15.9027 6.49503 15.4647 5.19156 15.0368 4.27453C14.8218 3.8139 14.6063 3.44421 14.4428 3.18724C14.3609 3.05857 14.2917 2.95762 14.2416 2.88709C14.2166 2.8518 14.1963 2.82408 14.1815 2.80426L14.1636 2.78048L14.158 2.7731L14.156 2.77055L14.1552 2.76956L14.1549 2.76914C14.1548 2.76895 14.1546 2.76877 13.5996 3.20045L14.1546 2.76877ZM11.7552 5.16879C11.5168 4.86227 11.075 4.80705 10.7685 5.04546C10.4628 5.28321 10.4071 5.72319 10.6432 6.02961L10.6452 6.03231C10.6481 6.03609 10.6535 6.04353 10.6613 6.05445C10.6768 6.07633 10.7014 6.11199 10.732 6.1601C10.7935 6.2567 10.878 6.4013 10.963 6.58353C11.1351 6.95221 11.2971 7.44874 11.2971 8.00047C11.2971 8.5522 11.1351 9.04873 10.963 9.41741C10.878 9.59964 10.7935 9.74424 10.732 9.84084C10.7014 9.88895 10.6768 9.92461 10.6613 9.94648C10.6535 9.95741 10.6481 9.96484 10.6452 9.96863L10.6432 9.97132C10.4071 10.2777 10.4628 10.7177 10.7685 10.9555C11.075 11.1939 11.5168 11.1387 11.7552 10.8321L11.2002 10.4005C11.7552 10.8321 11.7553 10.832 11.7555 10.8318L11.7558 10.8314L11.7564 10.8305L11.758 10.8286L11.7619 10.8234L11.7731 10.8085C11.782 10.7966 11.7937 10.7806 11.8078 10.7607C11.8361 10.721 11.874 10.6656 11.9184 10.5958C12.0069 10.4567 12.1224 10.2584 12.2374 10.0121C12.4653 9.52364 12.7033 8.82017 12.7033 8.00047C12.7033 7.18077 12.4653 6.4773 12.2374 5.98884C12.1224 5.74249 12.0069 5.54424 11.9184 5.40512C11.874 5.33538 11.8361 5.27996 11.8078 5.24023C11.7937 5.22035 11.782 5.20435 11.7731 5.1924L11.7619 5.17752L11.758 5.17238L11.7564 5.17039L11.7558 5.16954L11.7555 5.16916C11.7553 5.16897 11.7552 5.16879 11.2002 5.60047L11.7552 5.16879Z" fill="#8D99A5"/>
</svg>

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1.18262 0.960693L14.3815 14.1596" stroke="#8D99A5" stroke-width="1.61751" stroke-miterlimit="10" stroke-linecap="round"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.57061 4.20635L5.19539 4.51904H3.58059L10.2419 11.1804V8.87764L5.57061 4.20635ZM10.2419 6.59013V2.15546C10.2419 1.42405 9.38884 1.0245 8.82695 1.49274L6.81834 3.16658L10.2419 6.59013ZM5.19526 11.2479H2.7146C1.76172 11.2479 0.989258 10.4754 0.989258 9.52254V6.24438C0.989258 5.69051 1.25024 5.1976 1.65595 4.88191L10.2419 13.4679V13.6117C10.2419 14.3431 9.38884 14.7426 8.82695 14.2744L5.19526 11.248V11.2479Z" fill="#8D99A5"/>
</svg>

After

Width:  |  Height:  |  Size: 713 B

View file

@ -14,18 +14,23 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import {User} from "matrix-js-sdk/src/models/user";
import {Room} from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
import { Room } from "matrix-js-sdk/src/models/room";
import DMRoomMap from './utils/DMRoomMap';
import {mediaFromMxc} from "./customisations/Media";
import { mediaFromMxc } from "./customisations/Media";
import SettingsStore from "./settings/SettingsStore";
export type ResizeMethod = "crop" | "scale";
// Not to be used for BaseAvatar urls as that has similar default avatar fallback already
export function avatarUrlForMember(member: RoomMember, width: number, height: number, resizeMethod: ResizeMethod) {
export function avatarUrlForMember(
member: RoomMember,
width: number,
height: number,
resizeMethod: ResizeMethod,
): string {
let url: string;
if (member?.getMxcAvatarUrl()) {
url = mediaFromMxc(member.getMxcAvatarUrl()).getThumbnailOfSourceHttp(width, height, resizeMethod);
@ -39,7 +44,12 @@ export function avatarUrlForMember(member: RoomMember, width: number, height: nu
return url;
}
export function avatarUrlForUser(user: User, width: number, height: number, resizeMethod?: ResizeMethod) {
export function avatarUrlForUser(
user: Pick<User, "avatarUrl">,
width: number,
height: number,
resizeMethod?: ResizeMethod,
): string | null {
if (!user.avatarUrl) return null;
return mediaFromMxc(user.avatarUrl).getThumbnailOfSourceHttp(width, height, resizeMethod);
}

View file

@ -99,7 +99,7 @@ const CHECK_PROTOCOLS_ATTEMPTS = 3;
// (and store the ID of their native room)
export const VIRTUAL_ROOM_EVENT_TYPE = 'im.vector.is_virtual_room';
enum AudioID {
export enum AudioID {
Ring = 'ringAudio',
Ringback = 'ringbackAudio',
CallEnd = 'callendAudio',

View file

@ -468,7 +468,7 @@ function restoreEncryptionInfo(searchResultSlice = []) {
ev.event.curve25519Key,
ev.event.ed25519Key,
);
ev._forwardingCurve25519KeyChain = ev.event.forwardingCurve25519KeyChain;
ev.forwardingCurve25519KeyChain = ev.event.forwardingCurve25519KeyChain;
delete ev.event.curve25519Key;
delete ev.event.ed25519Key;

View file

@ -150,6 +150,10 @@ function success(promise?: Promise<any>) {
return {promise};
}
function successSync(value: any) {
return success(Promise.resolve(value));
}
/* Disable the "unexpected this" error for these commands - all of the run
* functions are called with `this` bound to the Command instance.
*/
@ -160,7 +164,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends the given message as a spoiler'),
runFn: function(roomId, message) {
return success(ContentHelpers.makeHtmlMessage(
return successSync(ContentHelpers.makeHtmlMessage(
message,
`<span data-mx-spoiler>${message}</span>`,
));
@ -176,7 +180,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -189,7 +193,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -202,7 +206,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -215,7 +219,7 @@ export const Commands = [
if (args) {
message = message + ' ' + args;
}
return success(ContentHelpers.makeTextMessage(message));
return successSync(ContentHelpers.makeTextMessage(message));
},
category: CommandCategories.messages,
}),
@ -224,7 +228,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends a message as plain text, without interpreting it as markdown'),
runFn: function(roomId, messages) {
return success(ContentHelpers.makeTextMessage(messages));
return successSync(ContentHelpers.makeTextMessage(messages));
},
category: CommandCategories.messages,
}),
@ -233,7 +237,7 @@ export const Commands = [
args: '<message>',
description: _td('Sends a message as html, without interpreting it as markdown'),
runFn: function(roomId, messages) {
return success(ContentHelpers.makeHtmlMessage(messages, messages));
return successSync(ContentHelpers.makeHtmlMessage(messages, messages));
},
category: CommandCategories.messages,
}),
@ -978,7 +982,7 @@ export const Commands = [
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
return success(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
return successSync(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
}),
@ -988,7 +992,7 @@ export const Commands = [
args: '<message>',
runFn: function(roomId, args) {
if (!args) return reject(this.getUserId());
return success(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
return successSync(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
},
category: CommandCategories.messages,
}),

View file

@ -24,7 +24,9 @@ import { Room } from 'matrix-js-sdk/src/models/room';
// is sip virtual: there could be others in the future.
export default class VoipUserMapper {
private virtualRoomIdCache = new Set<string>();
// We store mappings of virtual -> native room IDs here until the local echo for the
// account data arrives.
private virtualToNativeRoomIdCache = new Map<string, string>();
public static sharedInstance(): VoipUserMapper {
if (window.mxVoipUserMapper === undefined) window.mxVoipUserMapper = new VoipUserMapper();
@ -49,10 +51,20 @@ export default class VoipUserMapper {
native_room: roomId,
});
this.virtualToNativeRoomIdCache.set(virtualRoomId, roomId);
return virtualRoomId;
}
public nativeRoomForVirtualRoom(roomId: string): string {
const cachedNativeRoomId = this.virtualToNativeRoomIdCache.get(roomId);
if (cachedNativeRoomId) {
console.log(
"Returning native room ID " + cachedNativeRoomId + " for virtual room ID " + roomId + " from cache",
);
return cachedNativeRoomId;
}
const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
if (!virtualRoom) return null;
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
@ -67,7 +79,7 @@ export default class VoipUserMapper {
public isVirtualRoom(room: Room): boolean {
if (this.nativeRoomForVirtualRoom(room.roomId)) return true;
if (this.virtualRoomIdCache.has(room.roomId)) return true;
if (this.virtualToNativeRoomIdCache.has(room.roomId)) return true;
// also look in the create event for the claimed native room ID, which is the only
// way we can recognise a virtual room we've created when it first arrives down
@ -110,7 +122,7 @@ export default class VoipUserMapper {
// also put this room in the virtual room ID cache so isVirtualRoom return the right answer
// in however long it takes for the echo of setAccountData to come down the sync
this.virtualRoomIdCache.add(invitedRoom.roomId);
this.virtualToNativeRoomIdCache.set(invitedRoom.roomId, nativeRoom.roomId);
}
}
}

View file

@ -20,19 +20,19 @@ limitations under the License.
import React from 'react';
import { _t } from '../languageHandler';
import AutocompleteProvider from './AutocompleteProvider';
import {PillCompletion} from './Components';
import { PillCompletion } from './Components';
import * as sdk from '../index';
import QueryMatcher from './QueryMatcher';
import {sortBy} from 'lodash';
import {MatrixClientPeg} from '../MatrixClientPeg';
import { sortBy } from 'lodash';
import { MatrixClientPeg } from '../MatrixClientPeg';
import MatrixEvent from "matrix-js-sdk/src/models/event";
import Room from "matrix-js-sdk/src/models/room";
import RoomMember from "matrix-js-sdk/src/models/room-member";
import RoomState from "matrix-js-sdk/src/models/room-state";
import EventTimeline from "matrix-js-sdk/src/models/event-timeline";
import {makeUserPermalink} from "../utils/permalinks/Permalinks";
import {ICompletion, ISelectionRange} from "./Autocompleter";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { RoomState } from "matrix-js-sdk/src/models/room-state";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { makeUserPermalink } from "../utils/permalinks/Permalinks";
import { ICompletion, ISelectionRange } from "./Autocompleter";
const USER_REGEX = /\B@\S*/g;

View file

@ -1953,6 +1953,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// Create and start the client
await Lifecycle.setLoggedIn(credentials);
await this.postLoginSetup();
PerformanceMonitor.instance.stop(PerformanceEntryNames.LOGIN);
PerformanceMonitor.instance.stop(PerformanceEntryNames.REGISTER);
};

View file

@ -41,7 +41,7 @@ export function getUnsentMessages(room) {
}
@replaceableComponent("structures.RoomStatusBar")
export default class RoomStatusBar extends React.Component {
export default class RoomStatusBar extends React.PureComponent {
static propTypes = {
// the room this statusbar is representing.
room: PropTypes.object.isRequired,

View file

@ -25,6 +25,7 @@ import React, { createRef } from 'react';
import classNames from 'classnames';
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { SearchResult } from "matrix-js-sdk/src/models/search-result";
import { EventSubscription } from "fbemitter";
import shouldHideEvent from '../../shouldHideEvent';
@ -80,7 +81,6 @@ import { objectHasDiff } from "../../utils/objects";
import SpaceRoomView from "./SpaceRoomView";
import { IOpts } from "../../createRoom";
import { replaceableComponent } from "../../utils/replaceableComponent";
import { omit } from 'lodash';
import UIStore from "../../stores/UIStore";
const DEBUG = false;
@ -143,7 +143,7 @@ export interface IState {
searchResults?: XOR<{}, {
count: number;
highlights: string[];
results: MatrixEvent[];
results: SearchResult[];
next_batch: string; // eslint-disable-line camelcase
}>;
searchHighlights?: string[];
@ -572,16 +572,12 @@ export default class RoomView extends React.Component<IProps, IState> {
shouldComponentUpdate(nextProps, nextState) {
const hasPropsDiff = objectHasDiff(this.props, nextProps);
// React only shallow comparison and we only want to trigger
// a component re-render if a room requires an upgrade
const newUpgradeRecommendation = nextState.upgradeRecommendation || {}
const state = omit(this.state, ['upgradeRecommendation']);
const newState = omit(nextState, ['upgradeRecommendation'])
const { upgradeRecommendation, ...state } = this.state;
const { upgradeRecommendation: newUpgradeRecommendation, ...newState } = nextState;
const hasStateDiff =
objectHasDiff(state, newState) ||
(newUpgradeRecommendation.needsUpgrade === true)
newUpgradeRecommendation?.needsUpgrade !== upgradeRecommendation?.needsUpgrade ||
objectHasDiff(state, newState);
return hasPropsDiff || hasStateDiff;
}
@ -701,16 +697,11 @@ export default class RoomView extends React.Component<IProps, IState> {
room_id: this.state.room.roomId,
event_id: this.state.initialEventId,
highlighted: false,
replyingToEvent: this.state.replyToEvent,
});
}
}
private onLayoutChange = () => {
this.setState({
layout: SettingsStore.getValue("layout"),
});
};
private onRightPanelStoreUpdate = () => {
this.setState({
showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom,
@ -1644,29 +1635,27 @@ export default class RoomView extends React.Component<IProps, IState> {
let auxPanelMaxHeight = UIStore.instance.windowHeight -
(54 + // height of RoomHeader
36 + // height of the status area
51 + // minimum height of the message compmoser
51 + // minimum height of the message composer
120); // amount of desired scrollback
// XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway
// but it's better than the video going missing entirely
if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50;
this.setState({auxPanelMaxHeight: auxPanelMaxHeight});
if (this.state.auxPanelMaxHeight !== auxPanelMaxHeight) {
this.setState({ auxPanelMaxHeight });
}
};
private onStatusBarVisible = () => {
if (this.unmounted) return;
this.setState({
statusBarVisible: true,
});
if (this.unmounted || this.state.statusBarVisible) return;
this.setState({ statusBarVisible: true });
};
private onStatusBarHidden = () => {
// This is currently not desired as it is annoying if it keeps expanding and collapsing
if (this.unmounted) return;
this.setState({
statusBarVisible: false,
});
if (this.unmounted || !this.state.statusBarVisible) return;
this.setState({ statusBarVisible: false });
};
/**

View file

@ -18,14 +18,14 @@ limitations under the License.
*/
import SettingsStore from "../../settings/SettingsStore";
import {LayoutPropType} from "../../settings/Layout";
import React, {createRef} from 'react';
import { LayoutPropType } from "../../settings/Layout";
import React, { createRef } from 'react';
import ReactDOM from "react-dom";
import PropTypes from 'prop-types';
import {EventTimeline} from "matrix-js-sdk/src/models/event-timeline";
import {TimelineWindow} from "matrix-js-sdk/src/timeline-window";
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline";
import { TimelineWindow } from "matrix-js-sdk/src/timeline-window";
import { _t } from '../../languageHandler';
import {MatrixClientPeg} from "../../MatrixClientPeg";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import RoomContext from "../../contexts/RoomContext";
import UserActivity from "../../UserActivity";
import Modal from "../../Modal";
@ -35,10 +35,11 @@ import { Key } from '../../Keyboard';
import Timer from '../../utils/Timer';
import shouldHideEvent from '../../shouldHideEvent';
import EditorStateTransfer from '../../utils/EditorStateTransfer';
import {haveTileForEvent} from "../views/rooms/EventTile";
import {UIFeature} from "../../settings/UIFeature";
import {replaceableComponent} from "../../utils/replaceableComponent";
import { haveTileForEvent } from "../views/rooms/EventTile";
import { UIFeature } from "../../settings/UIFeature";
import { replaceableComponent } from "../../utils/replaceableComponent";
import { arrayFastClone } from "../../utils/arrays";
import { Action } from "../../dispatcher/actions";
const PAGINATE_SIZE = 20;
const INITIAL_SIZE = 20;
@ -439,21 +440,42 @@ class TimelinePanel extends React.Component {
};
onAction = payload => {
if (payload.action === 'ignore_state_changed') {
this.forceUpdate();
}
if (payload.action === "edit_event") {
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
this.setState({editState}, () => {
if (payload.event && this._messagePanel.current) {
this._messagePanel.current.scrollToEventIfNeeded(
payload.event.getId(),
);
switch (payload.action) {
case "ignore_state_changed":
this.forceUpdate();
break;
case "edit_event": {
const editState = payload.event ? new EditorStateTransfer(payload.event) : null;
this.setState({editState}, () => {
if (payload.event && this._messagePanel.current) {
this._messagePanel.current.scrollToEventIfNeeded(
payload.event.getId(),
);
}
});
break;
}
case Action.ComposerInsert: {
// re-dispatch to the correct composer
if (this.state.editState) {
dis.dispatch({
...payload,
action: "edit_composer_insert",
});
} else {
dis.dispatch({
...payload,
action: "send_composer_insert",
});
}
});
}
if (payload.action === "scroll_to_bottom") {
this.jumpToLiveTimeline();
break;
}
case "scroll_to_bottom":
this.jumpToLiveTimeline();
break;
}
};

View file

@ -33,6 +33,8 @@ import { EventType } from "matrix-js-sdk/src/@types/event";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { ReadPinsEventId } from "../right_panel/PinnedMessagesCard";
import ForwardDialog from "../dialogs/ForwardDialog";
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { Action } from "../../../dispatcher/actions";
export function canCancel(eventStatus) {
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
@ -199,8 +201,8 @@ export default class MessageContextMenu extends React.Component {
};
onQuoteClick = () => {
dis.dispatch({
action: 'quote',
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
event: this.props.mxEvent,
});
this.closeMenu();

View file

@ -525,11 +525,11 @@ class RoomStateExplorer extends React.PureComponent<IExplorerProps, IRoomStateEx
}
interface IAccountDataExplorerState {
[inputId: string]: boolean | string | any;
isRoomAccountData: boolean;
event?: MatrixEvent;
editing: boolean;
queryEventType: string;
[inputId: string]: boolean | string;
}
class AccountDataExplorer extends React.PureComponent<IExplorerProps, IAccountDataExplorerState> {

View file

@ -162,6 +162,7 @@ const ForwardDialog: React.FC<IProps> = ({ matrixClient: cli, event, permalinkCr
});
mockEvent.sender = {
name: profileInfo.displayname || userId,
rawDisplayName: profileInfo.displayname,
userId,
getAvatarUrl: (..._) => {
return avatarUrlForUser(

View file

@ -153,8 +153,8 @@ class ThreepidMember extends Member {
}
interface IDMUserTileProps {
member: RoomMember;
onRemove(member: RoomMember): void;
member: Member;
onRemove(member: Member): void;
}
class DMUserTile extends React.PureComponent<IDMUserTileProps> {
@ -168,7 +168,7 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
render() {
const avatarSize = 20;
const avatar = this.props.member.isEmail
const avatar = (this.props.member as ThreepidMember).isEmail
? <img
className='mx_InviteDialog_userTile_avatar mx_InviteDialog_userTile_threepidAvatar'
src={require("../../../../res/img/icon-email-pill-avatar.svg")}
@ -210,9 +210,9 @@ class DMUserTile extends React.PureComponent<IDMUserTileProps> {
}
interface IDMRoomTileProps {
member: RoomMember;
member: Member;
lastActiveTs: number;
onToggle(member: RoomMember): void;
onToggle(member: Member): void;
highlightWord: string;
isSelected: boolean;
}
@ -270,7 +270,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
}
const avatarSize = 36;
const avatar = this.props.member.isEmail
const avatar = (this.props.member as ThreepidMember).isEmail
? <img
src={require("../../../../res/img/icon-email-pill-avatar.svg")}
width={avatarSize} height={avatarSize} />
@ -298,7 +298,7 @@ class DMRoomTile extends React.PureComponent<IDMRoomTileProps> {
</span>
);
const caption = this.props.member.isEmail
const caption = (this.props.member as ThreepidMember).isEmail
? _t("Invite by email")
: this.highlightName(this.props.member.userId);
@ -334,7 +334,7 @@ interface IInviteDialogProps {
}
interface IInviteDialogState {
targets: RoomMember[]; // array of Member objects (see interface above)
targets: Member[]; // array of Member objects (see interface above)
filterText: string;
recents: { user: Member, userId: string }[];
numRecentsShown: number;

View file

@ -24,7 +24,7 @@ import { _t } from '../../../languageHandler';
import { formatCommaSeparatedList } from '../../../utils/FormattingUtils';
import { isValid3pidInvite } from "../../../RoomInvite";
import EventListSummary from "./EventListSummary";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
// An array of member events to summarise
@ -303,7 +303,7 @@ export default class MemberEventListSummary extends React.Component<IProps> {
return res;
}
private static getTransitionSequence(events: MatrixEvent[]) {
private static getTransitionSequence(events: IUserEvents[]) {
return events.map(MemberEventListSummary.getTransition);
}
@ -315,7 +315,7 @@ export default class MemberEventListSummary extends React.Component<IProps> {
* @returns {string?} the transition type given to this event. This defaults to `null`
* if a transition is not recognised.
*/
private static getTransition(e: MatrixEvent): TransitionType {
private static getTransition(e: IUserEvents): TransitionType {
if (e.mxEvent.getType() === 'm.room.third_party_invite') {
// Handle 3pid invites the same as invites so they get bundled together
if (!isValid3pidInvite(e.mxEvent)) {

View file

@ -297,6 +297,7 @@ export default class ReplyThread extends React.Component {
}
async getEvent(eventId) {
if (!eventId) return null;
const event = this.room.findEventById(eventId);
if (event) return event;
@ -392,6 +393,7 @@ export default class ReplyThread extends React.Component {
alwaysShowTimestamps={this.props.alwaysShowTimestamps}
enableFlair={SettingsStore.getValue(UIFeature.Flair)}
replacingEventId={ev.replacingEventId()}
as="div"
/>
</blockquote>;
});

View file

@ -24,6 +24,7 @@ import {_t} from "../../../languageHandler";
import {mediaFromContent} from "../../../customisations/Media";
import {decryptFile} from "../../../utils/DecryptFile";
import RecordingPlayback from "../voice_messages/RecordingPlayback";
import {IMediaEventContent} from "../../../customisations/models/IMediaEventContent";
interface IProps {
mxEvent: MatrixEvent;
@ -45,7 +46,7 @@ export default class MVoiceMessageBody extends React.PureComponent<IProps, IStat
public async componentDidMount() {
let buffer: ArrayBuffer;
const content = this.props.mxEvent.getContent();
const content: IMediaEventContent = this.props.mxEvent.getContent();
const media = mediaFromContent(content);
if (media.isEncrypted) {
try {

View file

@ -16,20 +16,19 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import {formatFullDate, formatTime, formatFullTime} from '../../../DateUtils';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { formatFullDate, formatTime, formatFullTime } from '../../../DateUtils';
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
ts: number;
showTwelveHour?: boolean;
showFullDate?: boolean;
showSeconds?: boolean;
}
@replaceableComponent("views.messages.MessageTimestamp")
export default class MessageTimestamp extends React.Component {
static propTypes = {
ts: PropTypes.number.isRequired,
showTwelveHour: PropTypes.bool,
showFullDate: PropTypes.bool,
showSeconds: PropTypes.bool,
};
render() {
export default class MessageTimestamp extends React.Component<IProps> {
public render() {
const date = new Date(this.props.ts);
let timestamp;
if (this.props.showFullDate) {
@ -41,7 +40,11 @@ export default class MessageTimestamp extends React.Component {
}
return (
<span className="mx_MessageTimestamp" title={formatFullDate(date, this.props.showTwelveHour)} aria-hidden={true}>
<span
className="mx_MessageTimestamp"
title={formatFullDate(date, this.props.showTwelveHour)}
aria-hidden={true}
>
{timestamp}
</span>
);

View file

@ -17,10 +17,10 @@
import React from 'react';
import Flair from '../elements/Flair.js';
import FlairStore from '../../../stores/FlairStore';
import {getUserNameColorClass} from '../../../utils/FormattingUtils';
import { getUserNameColorClass } from '../../../utils/FormattingUtils';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import MatrixEvent from "matrix-js-sdk/src/models/event";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
interface IProps {
mxEvent: MatrixEvent;

View file

@ -16,12 +16,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import React, { createRef } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import highlight from 'highlight.js';
import * as HtmlUtils from '../../../HtmlUtils';
import {formatDate} from '../../../DateUtils';
import { formatDate } from '../../../DateUtils';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import dis from '../../../dispatcher/dispatcher';
@ -29,14 +29,16 @@ import { _t } from '../../../languageHandler';
import * as ContextMenu from '../../structures/ContextMenu';
import SettingsStore from "../../../settings/SettingsStore";
import ReplyThread from "../elements/ReplyThread";
import {pillifyLinks, unmountPills} from '../../../utils/pillify';
import {IntegrationManagers} from "../../../integrations/IntegrationManagers";
import {isPermalinkHost} from "../../../utils/permalinks/Permalinks";
import {toRightOf} from "../../structures/ContextMenu";
import {copyPlaintext} from "../../../utils/strings";
import { pillifyLinks, unmountPills } from '../../../utils/pillify';
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import { isPermalinkHost } from "../../../utils/permalinks/Permalinks";
import { toRightOf } from "../../structures/ContextMenu";
import { copyPlaintext } from "../../../utils/strings";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import UIStore from "../../../stores/UIStore";
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { Action } from "../../../dispatcher/actions";
@replaceableComponent("views.messages.TextualBody")
export default class TextualBody extends React.Component {
@ -390,9 +392,9 @@ export default class TextualBody extends React.Component {
onEmoteSenderClick = event => {
const mxEvent = this.props.mxEvent;
dis.dispatch({
action: 'insert_mention',
user_id: mxEvent.getSender(),
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: mxEvent.getSender(),
});
};

View file

@ -17,8 +17,9 @@ limitations under the License.
import React from "react";
import * as sdk from "../../../index";
import {_t} from "../../../languageHandler";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { _t } from "../../../languageHandler";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
export const PendingActionSpinner = ({text}) => {
const Spinner = sdk.getComponent('elements.Spinner');
@ -31,7 +32,7 @@ export const PendingActionSpinner = ({text}) => {
interface IProps {
waitingForOtherParty: boolean;
waitingForNetwork: boolean;
member: RoomMember;
member: RoomMember | User;
onStartVerification: () => Promise<void>;
isRoomEncrypted: boolean;
inDialog: boolean;
@ -55,7 +56,7 @@ const EncryptionInfo: React.FC<IProps> = ({
text = _t("Accept on your other login…");
} else {
text = _t("Waiting for %(displayName)s to accept…", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
} else {

View file

@ -38,7 +38,7 @@ import SettingsStore from "../../../settings/SettingsStore";
import RoomViewStore from "../../../stores/RoomViewStore";
import MultiInviter from "../../../utils/MultiInviter";
import GroupStore from "../../../stores/GroupStore";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import E2EIcon from "../rooms/E2EIcon";
import { useEventEmitter } from "../../../hooks/useEventEmitter";
import { textualPowerLevel } from '../../../Roles';
@ -68,6 +68,7 @@ import RoomAvatar from "../avatars/RoomAvatar";
import RoomName from "../elements/RoomName";
import { mediaFromMxc } from "../../../customisations/Media";
import UIStore from "../../../stores/UIStore";
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
export interface IDevice {
deviceId: string;
@ -146,7 +147,7 @@ async function openDMForUser(matrixClient: MatrixClient, userId: string) {
type SetUpdating = (updating: boolean) => void;
function useHasCrossSigningKeys(cli: MatrixClient, member: RoomMember, canVerify: boolean, setUpdating: SetUpdating) {
function useHasCrossSigningKeys(cli: MatrixClient, member: User, canVerify: boolean, setUpdating: SetUpdating) {
return useAsyncMemo(async () => {
if (!canVerify) {
return undefined;
@ -368,9 +369,9 @@ const UserOptionsSection: React.FC<{
};
const onInsertPillButton = function() {
dis.dispatch({
action: 'insert_mention',
user_id: member.userId,
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: member.userId,
});
};
@ -971,7 +972,7 @@ interface IRoomPermissions {
canInvite: boolean;
}
function useRoomPermissions(cli: MatrixClient, room: Room, user: User): IRoomPermissions {
function useRoomPermissions(cli: MatrixClient, room: Room, user: RoomMember): IRoomPermissions {
const [roomPermissions, setRoomPermissions] = useState<IRoomPermissions>({
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
modifyLevelMax: -1,
@ -1028,7 +1029,7 @@ function useRoomPermissions(cli: MatrixClient, room: Room, user: User): IRoomPer
}
const PowerLevelSection: React.FC<{
user: User;
user: RoomMember;
room: Room;
roomPermissions: IRoomPermissions;
powerLevels: IPowerLevelsContent;
@ -1037,7 +1038,7 @@ const PowerLevelSection: React.FC<{
return (<PowerLevelEditor user={user} room={room} roomPermissions={roomPermissions} />);
} else {
const powerLevelUsersDefault = powerLevels.users_default || 0;
const powerLevel = parseInt(user.powerLevel, 10);
const powerLevel = user.powerLevel;
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
return (
<div className="mx_UserInfo_profileField">
@ -1048,13 +1049,13 @@ const PowerLevelSection: React.FC<{
};
const PowerLevelEditor: React.FC<{
user: User;
user: RoomMember;
room: Room;
roomPermissions: IRoomPermissions;
}> = ({user, room, roomPermissions}) => {
const cli = useContext(MatrixClientContext);
const [selectedPowerLevel, setSelectedPowerLevel] = useState(parseInt(user.powerLevel, 10));
const [selectedPowerLevel, setSelectedPowerLevel] = useState(user.powerLevel);
const onPowerChange = useCallback(async (powerLevelStr: string) => {
const powerLevel = parseInt(powerLevelStr, 10);
setSelectedPowerLevel(powerLevel);
@ -1231,7 +1232,7 @@ const BasicUserInfo: React.FC<{
setPendingUpdateCount(pendingUpdateCount - 1);
}, [pendingUpdateCount]);
const roomPermissions = useRoomPermissions(cli, room, member);
const roomPermissions = useRoomPermissions(cli, room, member as RoomMember);
const onSynapseDeactivate = useCallback(async () => {
const {finished} = Modal.createTrackedDialog('Synapse User Deactivation', '', QuestionDialog, {
@ -1275,12 +1276,26 @@ const BasicUserInfo: React.FC<{
);
}
let memberDetails;
let adminToolsContainer;
if (room && member.roomId) {
if (room && (member as RoomMember).roomId) {
// hide the Roles section for DMs as it doesn't make sense there
if (!DMRoomMap.shared().getUserIdForRoomId((member as RoomMember).roomId)) {
memberDetails = <div className="mx_UserInfo_container">
<h3>{ _t("Role") }</h3>
<PowerLevelSection
powerLevels={powerLevels}
user={member as RoomMember}
room={room}
roomPermissions={roomPermissions}
/>
</div>;
}
adminToolsContainer = (
<RoomAdminToolsContainer
powerLevels={powerLevels}
member={member}
member={member as RoomMember}
room={room}
startUpdating={startUpdating}
stopUpdating={stopUpdating}>
@ -1309,20 +1324,6 @@ const BasicUserInfo: React.FC<{
spinner = <Spinner />;
}
let memberDetails;
// hide the Roles section for DMs as it doesn't make sense there
if (room && member.roomId && !DMRoomMap.shared().getUserIdForRoomId(member.roomId)) {
memberDetails = <div className="mx_UserInfo_container">
<h3>{ _t("Role") }</h3>
<PowerLevelSection
powerLevels={powerLevels}
user={member}
room={room}
roomPermissions={roomPermissions}
/>
</div>;
}
// only display the devices list if our client supports E2E
const cryptoEnabled = cli.isCryptoEnabled();
@ -1349,8 +1350,7 @@ const BasicUserInfo: React.FC<{
const setUpdating = (updating) => {
setPendingUpdateCount(count => count + (updating ? 1 : -1));
};
const hasCrossSigningKeys =
useHasCrossSigningKeys(cli, member, canVerify, setUpdating );
const hasCrossSigningKeys = useHasCrossSigningKeys(cli, member as User, canVerify, setUpdating);
const showDeviceListSpinner = devices === undefined;
if (canVerify) {
@ -1359,9 +1359,9 @@ const BasicUserInfo: React.FC<{
verifyButton = (
<AccessibleButton className="mx_UserInfo_field mx_UserInfo_verifyButton" onClick={() => {
if (hasCrossSigningKeys) {
verifyUser(member);
verifyUser(member as User);
} else {
legacyVerifyUser(member);
legacyVerifyUser(member as User);
}
}}>
{_t("Verify")}
@ -1409,7 +1409,7 @@ const BasicUserInfo: React.FC<{
<UserOptionsSection
canInvite={roomPermissions.canInvite}
isIgnored={isIgnored}
member={member}
member={member as RoomMember}
isSpace={SettingsStore.getValue("feature_spaces") && room?.isSpaceRoom()}
/>
@ -1428,13 +1428,15 @@ const UserInfoHeader: React.FC<{
const cli = useContext(MatrixClientContext);
const onMemberAvatarClick = useCallback(() => {
const avatarUrl = member.getMxcAvatarUrl ? member.getMxcAvatarUrl() : member.avatarUrl;
const avatarUrl = (member as RoomMember).getMxcAvatarUrl
? (member as RoomMember).getMxcAvatarUrl()
: (member as User).avatarUrl;
if (!avatarUrl) return;
const httpUrl = mediaFromMxc(avatarUrl).srcHttp;
const params = {
src: httpUrl,
name: member.name,
name: (member as RoomMember).name || (member as User).displayName,
};
Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true);
@ -1446,13 +1448,13 @@ const UserInfoHeader: React.FC<{
<div>
<MemberAvatar
key={member.userId} // to instantly blank the avatar when UserInfo changes members
member={member}
member={member as RoomMember}
width={2 * 0.3 * UIStore.instance.windowHeight} // 2x@30vh
height={2 * 0.3 * UIStore.instance.windowHeight} // 2x@30vh
resizeMethod="scale"
fallbackUserId={member.userId}
onClick={onMemberAvatarClick}
urls={member.avatarUrl ? [member.avatarUrl] : undefined} />
urls={(member as User).avatarUrl ? [(member as User).avatarUrl] : undefined} />
</div>
</div>
</div>
@ -1469,7 +1471,11 @@ const UserInfoHeader: React.FC<{
presenceCurrentlyActive = member.user.currentlyActive;
if (SettingsStore.getValue("feature_custom_status")) {
statusMessage = member.user._unstable_statusMessage;
if ((member as RoomMember).user) {
statusMessage = member.user.unstable_statusMessage;
} else {
statusMessage = (member as unknown as User).unstable_statusMessage;
}
}
}
@ -1500,7 +1506,7 @@ const UserInfoHeader: React.FC<{
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
}
const displayName = member.rawDisplayName || member.displayname;
const displayName = (member as RoomMember).rawDisplayName || (member as GroupMember).displayname;
return <React.Fragment>
{ avatarElement }

View file

@ -22,6 +22,7 @@ import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import {SCAN_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode";
import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
import {ReciprocateQRCode} from "matrix-js-sdk/src/crypto/verification/QRCode";
import {SAS} from "matrix-js-sdk/src/crypto/verification/SAS";
@ -51,7 +52,7 @@ enum VerificationPhase {
interface IProps {
layout: string;
request: VerificationRequest;
member: RoomMember;
member: RoomMember | User;
phase: VerificationPhase;
onClose: () => void;
isRoomEncrypted: boolean;
@ -134,7 +135,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
qrBlock = <div className="mx_UserInfo_container">
<h3>{_t("Verify by scanning")}</h3>
<p>{_t("Ask %(displayName)s to scan your code:", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
})}</p>
<div className="mx_VerificationPanel_qrCode">
@ -205,7 +206,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
const description = request.isSelfVerification ?
_t("Almost there! Is your other session showing the same shield?") :
_t("Almost there! Is %(displayName)s showing the same shield?", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
let body: JSX.Element;
if (this.state.reciprocateQREvent) {
@ -264,7 +265,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
}
} else {
description = _t("You've successfully verified %(displayName)s!", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
@ -302,7 +303,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
text = _t("You cancelled verification on your other session.");
} else {
text = _t("%(displayName)s cancelled verification.", {
displayName: member.displayName || member.name || member.userId,
displayName: (member as User).displayName || (member as RoomMember).name || member.userId,
});
}
text = `${text} ${startAgainInstruction}`;
@ -325,7 +326,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
public render() {
const {member, phase, request} = this.props;
const displayName = member.displayName || member.name || member.userId;
const displayName = (member as User).displayName || (member as RoomMember).name || member.userId;
switch (phase) {
case PHASE_READY:

View file

@ -82,13 +82,6 @@ export default class AppsDrawer extends React.Component {
this.props.resizeNotifier.off("isResizing", this.onIsResizing);
}
// TODO: [REACT-WARNING] Replace with appropriate lifecycle event
// eslint-disable-next-line camelcase
UNSAFE_componentWillReceiveProps(newProps) {
// Room has changed probably, update apps
this._updateApps();
}
onIsResizing = (resizing) => {
// This one is the vertical, ie. change height of apps drawer
this.setState({ resizingVertical: resizing });
@ -141,7 +134,10 @@ export default class AppsDrawer extends React.Component {
_getAppsHash = (apps) => apps.map(app => app.id).join("~");
componentDidUpdate(prevProps, prevState) {
if (this._getAppsHash(this.state.apps) !== this._getAppsHash(prevState.apps)) {
if (prevProps.userId !== this.props.userId || prevProps.room !== this.props.room) {
// Room has changed, update apps
this._updateApps();
} else if (this._getAppsHash(this.state.apps) !== this._getAppsHash(prevState.apps)) {
this._loadResizerPreferences();
}
}

View file

@ -15,19 +15,18 @@ limitations under the License.
*/
import React from 'react';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { Room } from 'matrix-js-sdk/src/models/room'
import dis from "../../../dispatcher/dispatcher";
import AppsDrawer from './AppsDrawer';
import classNames from 'classnames';
import RateLimitedFunc from '../../../ratelimitedfunc';
import SettingsStore from "../../../settings/SettingsStore";
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
import {UIFeature} from "../../../settings/UIFeature";
import { UIFeature } from "../../../settings/UIFeature";
import { ResizeNotifier } from "../../../utils/ResizeNotifier";
import CallViewForRoom from '../voip/CallViewForRoom';
import {objectHasDiff} from "../../../utils/objects";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { objectHasDiff } from "../../../utils/objects";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
// js-sdk room object
@ -69,19 +68,21 @@ export default class AuxPanel extends React.Component<IProps, IState> {
super(props);
this.state = {
counters: this._computeCounters(),
counters: this.computeCounters(),
};
}
componentDidMount() {
const cli = MatrixClientPeg.get();
cli.on("RoomState.events", this._rateLimitedUpdate);
if (SettingsStore.getValue("feature_state_counters")) {
cli.on("RoomState.events", this.rateLimitedUpdate);
}
}
componentWillUnmount() {
const cli = MatrixClientPeg.get();
if (cli) {
cli.removeListener("RoomState.events", this._rateLimitedUpdate);
if (cli && SettingsStore.getValue("feature_state_counters")) {
cli.removeListener("RoomState.events", this.rateLimitedUpdate);
}
}
@ -96,23 +97,11 @@ export default class AuxPanel extends React.Component<IProps, IState> {
}
}
onConferenceNotificationClick = (ev, type) => {
dis.dispatch({
action: 'place_call',
type: type,
room_id: this.props.room.roomId,
});
ev.stopPropagation();
ev.preventDefault();
};
_rateLimitedUpdate = new RateLimitedFunc(() => {
if (SettingsStore.getValue("feature_state_counters")) {
this.setState({counters: this._computeCounters()});
}
private rateLimitedUpdate = new RateLimitedFunc(() => {
this.setState({ counters: this.computeCounters() });
}, 500);
_computeCounters() {
private computeCounters() {
const counters = [];
if (this.props.room && SettingsStore.getValue("feature_state_counters")) {
@ -225,7 +214,7 @@ export default class AuxPanel extends React.Component<IProps, IState> {
}
return (
<AutoHideScrollbar className={classes} style={style} >
<AutoHideScrollbar className={classes} style={style}>
{ stateViews }
{ appsDrawer }
{ callView }

View file

@ -16,38 +16,39 @@ limitations under the License.
*/
import classNames from 'classnames';
import React, {createRef, ClipboardEvent} from 'react';
import {Room} from 'matrix-js-sdk/src/models/room';
import React, { createRef, ClipboardEvent } from 'react';
import { Room } from 'matrix-js-sdk/src/models/room';
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
import EMOTICON_REGEX from 'emojibase-regex/emoticon';
import EditorModel from '../../../editor/model';
import HistoryManager from '../../../editor/history';
import {Caret, setSelection} from '../../../editor/caret';
import { Caret, setSelection } from '../../../editor/caret';
import {
formatRangeAsQuote,
formatRangeAsCode,
toggleInlineFormat,
replaceRangeAndMoveCaret,
} from '../../../editor/operations';
import {getCaretOffsetAndText, getRangeForSelection} from '../../../editor/dom';
import Autocomplete, {generateCompletionDomId} from '../rooms/Autocomplete';
import {getAutoCompleteCreator} from '../../../editor/parts';
import {parsePlainTextMessage} from '../../../editor/deserialize';
import {renderModel} from '../../../editor/render';
import { getCaretOffsetAndText, getRangeForSelection } from '../../../editor/dom';
import Autocomplete, { generateCompletionDomId } from '../rooms/Autocomplete';
import { getAutoCompleteCreator } from '../../../editor/parts';
import { parseEvent, parsePlainTextMessage } from '../../../editor/deserialize';
import { renderModel } from '../../../editor/render';
import TypingStore from "../../../stores/TypingStore";
import SettingsStore from "../../../settings/SettingsStore";
import {Key} from "../../../Keyboard";
import {EMOTICON_TO_EMOJI} from "../../../emoji";
import {CommandCategories, CommandMap, parseCommandString} from "../../../SlashCommands";
import { Key } from "../../../Keyboard";
import { EMOTICON_TO_EMOJI } from "../../../emoji";
import { CommandCategories, CommandMap, parseCommandString } from "../../../SlashCommands";
import Range from "../../../editor/range";
import MessageComposerFormatBar from "./MessageComposerFormatBar";
import DocumentOffset from "../../../editor/offset";
import {IDiff} from "../../../editor/diff";
import { IDiff } from "../../../editor/diff";
import AutocompleteWrapperModel from "../../../editor/autocomplete";
import DocumentPosition from "../../../editor/position";
import {ICompletion} from "../../../autocomplete/Autocompleter";
import { ICompletion } from "../../../autocomplete/Autocompleter";
import { AutocompleteAction, getKeyBindingsManager, MessageComposerAction } from '../../../KeyBindingsManager';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { replaceableComponent } from "../../../utils/replaceableComponent";
// matches emoticons which follow the start of a line or whitespace
const REGEX_EMOTICON_WHITESPACE = new RegExp('(?:^|\\s)(' + EMOTICON_REGEX.source + ')\\s$');
@ -716,4 +717,48 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
focus() {
this.editorRef.current.focus();
}
public insertMention(userId: string) {
const {model} = this.props;
const {partCreator} = model;
const member = this.props.room.getMember(userId);
const displayName = member ?
member.rawDisplayName : userId;
const caret = this.getCaret();
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
// Insert suffix only if the caret is at the start of the composer
const parts = partCreator.createMentionParts(caret.offset === 0, displayName, userId);
model.transform(() => {
const addedLen = model.insert(parts, position);
return model.positionForOffset(caret.offset + addedLen, true);
});
// refocus on composer, as we just clicked "Mention"
this.focus();
}
public insertQuotedMessage(event: MatrixEvent) {
const {model} = this.props;
const {partCreator} = model;
const quoteParts = parseEvent(event, partCreator, {isQuotedMessage: true});
// add two newlines
quoteParts.push(partCreator.newline());
quoteParts.push(partCreator.newline());
model.transform(() => {
const addedLen = model.insert(quoteParts, model.positionForOffset(0));
return model.positionForOffset(addedLen, true);
});
// refocus on composer, as we just clicked "Quote"
this.focus();
}
public insertPlaintext(text: string) {
const {model} = this.props;
const {partCreator} = model;
const caret = this.getCaret();
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
model.transform(() => {
const addedLen = model.insert([partCreator.plain(text)], position);
return model.positionForOffset(caret.offset + addedLen, true);
});
}
}

View file

@ -16,25 +16,25 @@ limitations under the License.
*/
import React from 'react';
import * as sdk from '../../../index';
import {_t, _td} from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import PropTypes from 'prop-types';
import dis from '../../../dispatcher/dispatcher';
import EditorModel from '../../../editor/model';
import {getCaretOffsetAndText} from '../../../editor/dom';
import {htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand} from '../../../editor/serialize';
import {findEditableEvent} from '../../../utils/EventUtils';
import {parseEvent} from '../../../editor/deserialize';
import {CommandPartCreator} from '../../../editor/parts';
import { getCaretOffsetAndText } from '../../../editor/dom';
import { htmlSerializeIfNeeded, textSerialize, containsEmote, stripEmoteCommand } from '../../../editor/serialize';
import { findEditableEvent } from '../../../utils/EventUtils';
import { parseEvent } from '../../../editor/deserialize';
import { CommandPartCreator } from '../../../editor/parts';
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
import classNames from 'classnames';
import {EventStatus} from 'matrix-js-sdk/src/models/event';
import { EventStatus } from 'matrix-js-sdk/src/models/event';
import BasicMessageComposer from "./BasicMessageComposer";
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {CommandCategories, getCommand} from '../../../SlashCommands';
import {Action} from "../../../dispatcher/actions";
import { CommandCategories, getCommand } from '../../../SlashCommands';
import { Action } from "../../../dispatcher/actions";
import CountlyAnalytics from "../../../CountlyAnalytics";
import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { getKeyBindingsManager, MessageComposerAction } from '../../../KeyBindingsManager';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import SendHistoryManager from '../../../SendHistoryManager';
import Modal from '../../../Modal';
@ -124,6 +124,7 @@ export default class EditMessageComposer extends React.Component {
};
this._createEditorModel();
window.addEventListener("beforeunload", this._saveStoredEditorState);
this.dispatcherRef = dis.register(this.onAction);
}
_setEditorRef = ref => {
@ -399,6 +400,7 @@ export default class EditMessageComposer extends React.Component {
if (this._shouldSaveStoredEditorState) {
this._saveStoredEditorState();
}
dis.unregister(this.dispatcherRef);
}
_createEditorModel() {
@ -443,6 +445,18 @@ export default class EditMessageComposer extends React.Component {
});
};
onAction = payload => {
if (payload.action === "edit_composer_insert" && this._editorRef) {
if (payload.userId) {
this._editorRef.insertMention(payload.userId);
} else if (payload.event) {
this._editorRef.insertQuotedMessage(payload.event);
} else if (payload.text) {
this._editorRef.insertPlaintext(payload.text);
}
}
};
render() {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return (<div className={classNames("mx_EditMessageComposer", this.props.className)} onKeyDown={this._onKeyDown}>

View file

@ -46,6 +46,8 @@ import { EditorStateTransfer } from "../../../utils/EditorStateTransfer";
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
import {StaticNotificationState} from "../../../stores/notifications/StaticNotificationState";
import NotificationBadge from "./NotificationBadge";
import {ComposerInsertPayload} from "../../../dispatcher/payloads/ComposerInsertPayload";
import { Action } from '../../../dispatcher/actions';
const eventTileTypes = {
[EventType.RoomMessage]: 'messages.MessageEvent',
@ -376,7 +378,7 @@ export default class EventTile extends React.Component<IProps, IState> {
EventType.RoomMessage,
EventType.RoomMessageEncrypted,
];
if (!simpleSendableEvents.includes(this.props.mxEvent.getType())) return false;
if (!simpleSendableEvents.includes(this.props.mxEvent.getType() as EventType)) return false;
// Default case
return true;
@ -727,9 +729,9 @@ export default class EventTile extends React.Component<IProps, IState> {
onSenderProfileClick = event => {
const mxEvent = this.props.mxEvent;
dis.dispatch({
action: 'insert_mention',
user_id: mxEvent.getSender(),
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
userId: mxEvent.getSender(),
});
};

View file

@ -16,11 +16,11 @@ limitations under the License.
import React from 'react';
import classNames from 'classnames';
import { _t } from '../../../languageHandler';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
import { MatrixClientPeg } from '../../../MatrixClientPeg';
import * as sdk from '../../../index';
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import {Room} from "matrix-js-sdk/src/models/room";
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import dis from '../../../dispatcher/dispatcher';
import { ActionPayload } from "../../../dispatcher/payloads";
import Stickerpicker from './Stickerpicker';
@ -28,19 +28,21 @@ import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalin
import ContentMessages from '../../../ContentMessages';
import E2EIcon from './E2EIcon';
import SettingsStore from "../../../settings/SettingsStore";
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
import { aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu } from "../../structures/ContextMenu";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import ReplyPreview from "./ReplyPreview";
import {UIFeature} from "../../../settings/UIFeature";
import {UPDATE_EVENT} from "../../../stores/AsyncStore";
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { UIFeature } from "../../../settings/UIFeature";
import { UPDATE_EVENT } from "../../../stores/AsyncStore";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
import {RecordingState} from "../../../voice/VoiceRecording";
import Tooltip, {Alignment} from "../elements/Tooltip";
import { VoiceRecordingStore } from "../../../stores/VoiceRecordingStore";
import { RecordingState } from "../../../voice/VoiceRecording";
import Tooltip, { Alignment } from "../elements/Tooltip";
import ResizeNotifier from "../../../utils/ResizeNotifier";
import { E2EStatus } from '../../../utils/ShieldUtils';
import SendMessageComposer from "./SendMessageComposer";
import { ComposerInsertPayload } from "../../../dispatcher/payloads/ComposerInsertPayload";
import { Action } from "../../../dispatcher/actions";
interface IComposerAvatarProps {
me: object;
@ -316,10 +318,10 @@ export default class MessageComposer extends React.Component<IProps, IState> {
}
}
addEmoji(emoji) {
dis.dispatch({
action: "insert_emoji",
emoji,
addEmoji(emoji: string) {
dis.dispatch<ComposerInsertPayload>({
action: Action.ComposerInsert,
text: emoji,
});
}

View file

@ -24,7 +24,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {replaceableComponent} from "../../../utils/replaceableComponent";
@replaceableComponent("views.rooms.RoomUpgradeWarningBar")
export default class RoomUpgradeWarningBar extends React.Component {
export default class RoomUpgradeWarningBar extends React.PureComponent {
static propTypes = {
room: PropTypes.object.isRequired,
recommendation: PropTypes.object.isRequired,

View file

@ -27,27 +27,26 @@ import {
startsWith,
stripPrefix,
} from '../../../editor/serialize';
import {CommandPartCreator} from '../../../editor/parts';
import { CommandPartCreator } from '../../../editor/parts';
import BasicMessageComposer from "./BasicMessageComposer";
import ReplyThread from "../elements/ReplyThread";
import {parseEvent} from '../../../editor/deserialize';
import {findEditableEvent} from '../../../utils/EventUtils';
import { findEditableEvent } from '../../../utils/EventUtils';
import SendHistoryManager from "../../../SendHistoryManager";
import {CommandCategories, getCommand} from '../../../SlashCommands';
import { CommandCategories, getCommand } from '../../../SlashCommands';
import * as sdk from '../../../index';
import Modal from '../../../Modal';
import {_t, _td} from '../../../languageHandler';
import { _t, _td } from '../../../languageHandler';
import ContentMessages from '../../../ContentMessages';
import MatrixClientContext from "../../../contexts/MatrixClientContext";
import RateLimitedFunc from '../../../ratelimitedfunc';
import {Action} from "../../../dispatcher/actions";
import {containsEmoji} from "../../../effects/utils";
import {CHAT_EFFECTS} from '../../../effects';
import { Action } from "../../../dispatcher/actions";
import { containsEmoji } from "../../../effects/utils";
import { CHAT_EFFECTS } from '../../../effects';
import CountlyAnalytics from "../../../CountlyAnalytics";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import EMOJI_REGEX from 'emojibase-regex';
import {getKeyBindingsManager, MessageComposerAction} from '../../../KeyBindingsManager';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import { getKeyBindingsManager, MessageComposerAction } from '../../../KeyBindingsManager';
import { replaceableComponent } from "../../../utils/replaceableComponent";
import SettingsStore from '../../../settings/SettingsStore';
function addReplyToMessageContent(content, repliedToEvent, permalinkCreator) {
@ -486,62 +485,18 @@ export default class SendMessageComposer extends React.Component {
case Action.FocusComposer:
this._editorRef && this._editorRef.focus();
break;
case 'insert_mention':
this._insertMention(payload.user_id);
break;
case 'quote':
this._insertQuotedMessage(payload.event);
break;
case 'insert_emoji':
this._insertEmoji(payload.emoji);
case "send_composer_insert":
if (payload.userId) {
this._editorRef && this._editorRef.insertMention(payload.userId);
} else if (payload.event) {
this._editorRef && this._editorRef.insertQuotedMessage(payload.event);
} else if (payload.text) {
this._editorRef && this._editorRef.insertPlaintext(payload.text);
}
break;
}
};
_insertMention(userId) {
const {model} = this;
const {partCreator} = model;
const member = this.props.room.getMember(userId);
const displayName = member ?
member.rawDisplayName : userId;
const caret = this._editorRef.getCaret();
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
// Insert suffix only if the caret is at the start of the composer
const parts = partCreator.createMentionParts(caret.offset === 0, displayName, userId);
model.transform(() => {
const addedLen = model.insert(parts, position);
return model.positionForOffset(caret.offset + addedLen, true);
});
// refocus on composer, as we just clicked "Mention"
this._editorRef && this._editorRef.focus();
}
_insertQuotedMessage(event) {
const {model} = this;
const {partCreator} = model;
const quoteParts = parseEvent(event, partCreator, {isQuotedMessage: true});
// add two newlines
quoteParts.push(partCreator.newline());
quoteParts.push(partCreator.newline());
model.transform(() => {
const addedLen = model.insert(quoteParts, model.positionForOffset(0));
return model.positionForOffset(addedLen, true);
});
// refocus on composer, as we just clicked "Quote"
this._editorRef && this._editorRef.focus();
}
_insertEmoji = (emoji) => {
const {model} = this;
const {partCreator} = model;
const caret = this._editorRef.getCaret();
const position = model.positionForOffset(caret.offset, caret.atNodeEnd);
model.transform(() => {
const addedLen = model.insert([partCreator.plain(emoji)], position);
return model.positionForOffset(caret.offset + addedLen, true);
});
};
_onPaste = (event) => {
const {clipboardData} = event;
// Prioritize text on the clipboard over files as Office on macOS puts a bitmap

View file

@ -44,14 +44,11 @@ export default class BridgeSettingsTab extends React.Component<IProps> {
return <BridgeTile key={event.getId()} room={room} ev={event} />;
}
static getBridgeStateEvents(roomId: string) {
static getBridgeStateEvents(roomId: string): MatrixEvent[] {
const client = MatrixClientPeg.get();
const roomState = client.getRoom(roomId).currentState;
return BRIDGE_EVENT_TYPES.map(typeName => {
const events = roomState.events.get(typeName);
return events ? Array.from(events.values()) : [];
}).flat(1);
return BRIDGE_EVENT_TYPES.map(typeName => roomState.getStateEvents(typeName)).flat(1);
}
render() {

View file

@ -21,17 +21,20 @@ import {MatrixClientPeg} from '../../../MatrixClientPeg';
import dis from '../../../dispatcher/dispatcher';
import { _t } from '../../../languageHandler';
import { ActionPayload } from '../../../dispatcher/payloads';
import CallHandler from '../../../CallHandler';
import CallHandler, { AudioID } from '../../../CallHandler';
import RoomAvatar from '../avatars/RoomAvatar';
import FormButton from '../elements/FormButton';
import { CallState } from 'matrix-js-sdk/src/webrtc/call';
import {replaceableComponent} from "../../../utils/replaceableComponent";
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
import classNames from 'classnames';
interface IProps {
}
interface IState {
incomingCall: any;
silenced: boolean;
}
@replaceableComponent("views.voip.IncomingCallBox")
@ -44,6 +47,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
this.dispatcherRef = dis.register(this.onAction);
this.state = {
incomingCall: null,
silenced: false,
};
}
@ -58,6 +62,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
if (call && call.state === CallState.Ringing) {
this.setState({
incomingCall: call,
silenced: false, // Reset silenced state for new call
});
} else {
this.setState({
@ -84,6 +89,13 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
});
};
private onSilenceClick: React.MouseEventHandler = (e) => {
e.stopPropagation();
const newState = !this.state.silenced
this.setState({silenced: newState});
newState ? CallHandler.sharedInstance().pause(AudioID.Ring) : CallHandler.sharedInstance().play(AudioID.Ring);
}
public render() {
if (!this.state.incomingCall) {
return null;
@ -107,6 +119,12 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
}
}
const silenceClass = classNames({
"mx_IncomingCallBox_iconButton": true,
"mx_IncomingCallBox_unSilence": this.state.silenced,
"mx_IncomingCallBox_silence": !this.state.silenced,
});
return <div className="mx_IncomingCallBox">
<div className="mx_IncomingCallBox_CallerInfo">
<RoomAvatar
@ -118,6 +136,11 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
<h1>{caller}</h1>
<p>{incomingCallText}</p>
</div>
<AccessibleTooltipButton
className={silenceClass}
onClick={this.onSilenceClick}
title={this.state.silenced ? _t("Sound on"): _t("Silence call")}
/>
</div>
<div className="mx_IncomingCallBox_buttons">
<FormButton

View file

@ -159,4 +159,9 @@ export enum Action {
* Fired when joining a room failed
*/
JoinRoomError = "join_room_error",
/**
* Inserts content into the active composer. Should be used with ComposerInsertPayload
*/
ComposerInsert = "composer_insert",
}

View file

@ -0,0 +1,42 @@
/*
Copyright 2021 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 { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
interface IBaseComposerInsertPayload extends ActionPayload {
action: Action.ComposerInsert,
}
interface IComposerInsertMentionPayload extends IBaseComposerInsertPayload {
userId: string;
}
interface IComposerInsertQuotePayload extends IBaseComposerInsertPayload {
event: MatrixEvent;
}
interface IComposerInsertPlaintextPayload extends IBaseComposerInsertPayload {
text: string;
}
export type ComposerInsertPayload =
IComposerInsertMentionPayload |
IComposerInsertQuotePayload |
IComposerInsertPlaintextPayload;

View file

@ -17,6 +17,7 @@ limitations under the License.
import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import { Room } from "matrix-js-sdk/src/models/room";
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { User } from "matrix-js-sdk/src/models/user";
import { RightPanelPhases } from "../../stores/RightPanelStorePhases";
import { ActionPayload } from "../payloads";
import { Action } from "../actions";
@ -29,7 +30,7 @@ export interface SetRightPanelPhasePayload extends ActionPayload {
}
export interface SetRightPanelPhaseRefireParams {
member?: RoomMember;
member?: RoomMember | User;
verificationRequest?: VerificationRequest;
groupId?: string;
groupRoomId?: string;

View file

@ -15,13 +15,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {diffAtCaret, diffDeletion, IDiff} from "./diff";
import DocumentPosition, {IPosition} from "./position";
import { diffAtCaret, diffDeletion, IDiff } from "./diff";
import DocumentPosition, { IPosition } from "./position";
import Range from "./range";
import {SerializedPart, Part, PartCreator} from "./parts";
import AutocompleteWrapperModel, {ICallback} from "./autocomplete";
import { SerializedPart, Part, PartCreator } from "./parts";
import AutocompleteWrapperModel, { ICallback } from "./autocomplete";
import DocumentOffset from "./offset";
import {Caret} from "./caret";
import { Caret } from "./caret";
/**
* @callback ModelCallback
@ -390,7 +390,7 @@ export default class EditorModel {
return addLen;
}
positionForOffset(totalOffset: number, atPartEnd: boolean) {
positionForOffset(totalOffset: number, atPartEnd = false) {
let currentOffset = 0;
const index = this._parts.findIndex(part => {
const partLen = part.text.length;

View file

@ -21,11 +21,11 @@ import {Room} from "matrix-js-sdk/src/models/room";
import {useEventEmitter} from "./useEventEmitter";
const tryGetContent = (ev?: MatrixEvent) => ev ? ev.getContent() : undefined;
const tryGetContent = <T extends {}>(ev?: MatrixEvent) => ev ? ev.getContent<T>() : undefined;
// Hook to simplify listening to Matrix account data
export const useAccountData = <T extends {}>(cli: MatrixClient, eventType: string) => {
const [value, setValue] = useState<T>(() => tryGetContent(cli.getAccountData(eventType)));
const [value, setValue] = useState<T>(() => tryGetContent<T>(cli.getAccountData(eventType)));
const handler = useCallback((event) => {
if (event.getType() !== eventType) return;
@ -38,7 +38,7 @@ export const useAccountData = <T extends {}>(cli: MatrixClient, eventType: strin
// Hook to simplify listening to Matrix room account data
export const useRoomAccountData = <T extends {}>(room: Room, eventType: string) => {
const [value, setValue] = useState<T>(() => tryGetContent(room.getAccountData(eventType)));
const [value, setValue] = useState<T>(() => tryGetContent<T>(room.getAccountData(eventType)));
const handler = useCallback((event) => {
if (event.getType() !== eventType) return;

View file

@ -78,13 +78,13 @@
"Invite new community members": "Convida nous membres a la comunitat",
"Invite to Community": "Convida a la comunitat",
"Which rooms would you like to add to this community?": "Quines sales vols afegir a aquesta comunitat?",
"Show these rooms to non-members on the community page and room list?": "Vols mostrar aquestes sales a la pàgina de la comunitat i a la llista de sales per als que no hi son membres?",
"Show these rooms to non-members on the community page and room list?": "Voleu mostrar aquestes sales a la pàgina de la comunitat i al llistat de sales, als qui no en siguin membres?",
"Add rooms to the community": "Afegeix sales a la comunitat",
"Add to community": "Afegeix a la comunitat",
"Failed to invite the following users to %(groupId)s:": "No s'han pogut convidar a %(groupId)s els següents usuaris:",
"Failed to invite users to community": "No s'han pogut convidar els usuaris a la comunitat",
"Failed to invite users to community": "No s'ha pogut convidar els usuaris a la comunitat",
"Failed to invite users to %(groupId)s": "No s'han pogut convidar els usuaris a %(groupId)s",
"Failed to add the following rooms to %(groupId)s:": "No s'han pogut afegir a %(groupId)s les següents sales:",
"Failed to add the following rooms to %(groupId)s:": "No s'ha pogut afegir a %(groupId)s les següents sales:",
"%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s no té permís per enviar-te notificacions, comprova la configuració del teu navegador",
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s no ha rebut cap permís per enviar notificacions, torna-ho a provar",
"Unable to enable Notifications": "No s'han pogut activar les notificacions",
@ -371,23 +371,23 @@
"Communities": "Comunitats",
"Home": "Inici",
"Manage Integrations": "Gestiona les integracions",
"%(nameList)s %(transitionList)s": "%(transitionList)s%(nameList)s",
"%(nameList)s %(transitionList)s": "%(nameList)s %(transitionList)s",
"%(severalUsers)sjoined %(count)s times|one": "%(severalUsers)s s'hi han unit",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)s s'ha unit",
"%(oneUser)sjoined %(count)s times|one": "%(oneUser)ss'ha unit",
"%(severalUsers)sleft %(count)s times|one": "%(severalUsers)s han sortit",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)s ha sortit",
"%(oneUser)sleft %(count)s times|one": "%(oneUser)sha sortit",
"%(severalUsers)sjoined and left %(count)s times|other": "%(severalUsers)s s'hi han unit i han sortit %(count)s vegades",
"%(severalUsers)sjoined and left %(count)s times|one": "%(severalUsers)s s'hi han unit i han sortit",
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)s ha entrat i ha sortit %(count)s vegades",
"%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)s ha entrat i ha sortit",
"%(oneUser)sjoined and left %(count)s times|other": "%(oneUser)sha entrat i ha sortit %(count)s vegades",
"%(oneUser)sjoined and left %(count)s times|one": "%(oneUser)sha entrat i ha sortit",
"%(severalUsers)sleft and rejoined %(count)s times|other": "%(severalUsers)s han sortit i han tornat a entrar %(count)s vegades",
"%(severalUsers)sleft and rejoined %(count)s times|one": "%(severalUsers)s han sortit i han tornat a entrar",
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)s ha sortit i ha tornat a entrar %(count)s vegades",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)s ha sortit i ha tornat a entrar",
"%(oneUser)sleft and rejoined %(count)s times|other": "%(oneUser)sha sortit i ha tornat a entrar %(count)s vegades",
"%(oneUser)sleft and rejoined %(count)s times|one": "%(oneUser)sha sortit i ha tornat a entrar",
"%(severalUsers)srejected their invitations %(count)s times|other": "%(severalUsers)s han rebutjat les seves invitacions %(count)s vegades",
"%(severalUsers)srejected their invitations %(count)s times|one": "%(severalUsers)s han rebutjat les seves invitacions",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)s ha rebutjat la seva invitació %(count)s vegades",
"%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)s ha rebutjat la seva invitació",
"%(oneUser)srejected their invitation %(count)s times|other": "%(oneUser)sha rebutjat la seva invitació %(count)s vegades",
"%(oneUser)srejected their invitation %(count)s times|one": "%(oneUser)sha rebutjat la seva invitació",
"%(severalUsers)shad their invitations withdrawn %(count)s times|other": "S'han retirat les invitacions de %(severalUsers)s %(count)s vegades",
"%(severalUsers)shad their invitations withdrawn %(count)s times|one": "S'han retirat les invitacions de %(severalUsers)s",
"%(oneUser)shad their invitation withdrawn %(count)s times|other": "S'ha retirat la invitació de %(oneUser)s %(count)s vegades",
@ -410,12 +410,12 @@
"was kicked %(count)s times|one": "l'han fet fora",
"%(severalUsers)schanged their name %(count)s times|other": "%(severalUsers)s han canviat el seu nom %(count)s vegades",
"%(severalUsers)schanged their name %(count)s times|one": "%(severalUsers)s han canviat el seu nom",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)s ha canviat el seu nom %(count)s vegades",
"%(oneUser)schanged their name %(count)s times|other": "%(oneUser)sha canviat el seu nom %(count)s vegades",
"%(oneUser)schanged their name %(count)s times|one": "%(oneUser)s ha canviat el seu nom",
"%(severalUsers)schanged their avatar %(count)s times|other": "%(severalUsers)s han canviat el seu avatar %(count)s vegades",
"%(severalUsers)schanged their avatar %(count)s times|one": "%(severalUsers)s han canviat el seu avatar",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)s han canviat el seu avatar %(count)s vegades",
"%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)s ha canviat el seu avatar",
"%(oneUser)schanged their avatar %(count)s times|other": "%(oneUser)shan canviat el seu avatar %(count)s vegades",
"%(oneUser)schanged their avatar %(count)s times|one": "%(oneUser)sha canviat el seu avatar",
"%(items)s and %(count)s others|other": "%(items)s i %(count)s altres",
"%(items)s and %(count)s others|one": "%(items)s i un altre",
"%(items)s and %(lastItem)s": "%(items)s i %(lastItem)s",
@ -437,9 +437,9 @@
"Showing flair for these communities:": "Mostra els talents d'aquestes comunitats:",
"Display your community flair in rooms configured to show it.": "Mostra els talents de la vostra comunitat dins les sales configurades per a mostrar-los.",
"%(severalUsers)sjoined %(count)s times|other": "%(severalUsers)s han entrat %(count)s vegades",
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)s ha entrat %(count)s vegades",
"%(oneUser)sjoined %(count)s times|other": "%(oneUser)sha entrat %(count)s vegades",
"%(severalUsers)sleft %(count)s times|other": "%(severalUsers)s han sortit %(count)s vegades",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)s ha sortit %(count)s vegades",
"%(oneUser)sleft %(count)s times|other": "%(oneUser)sha sortit %(count)s vegades",
"Community IDs may only contain characters a-z, 0-9, or '=_-./'": "Les ID de les comunitats només poden contendre caràcters a-z, 0-9, o '=_-./'",
"Community IDs cannot be empty.": "Les ID de les comunitats no poden estar buides.",
"Something went wrong whilst creating your community": "S'ha produït un error mentre es creava la comunitat",
@ -722,7 +722,7 @@
"Failed to invite users to the room:": "No s'han pogut convidar els usuaris a la sala:",
"Missing roomId.": "Falta l'ID de sala.",
"Searches DuckDuckGo for results": "Cerca al DuckDuckGo els resultats",
"Changes your display nickname": "Canvia el teu àlies de visualització",
"Changes your display nickname": "Canvia l'àlies a mostrar",
"Invites user with given id to current room": "Convida a la sala actual l'usuari amb l'ID indicat",
"Kicks user with given id": "Expulsa l'usuari amb l'ID indicat",
"Bans user with given id": "Bandeja l'usuari amb l'ID indicat",
@ -854,7 +854,7 @@
"Changes your avatar in all rooms": "Canvia el teu avatar en totes les sales",
"Changes your avatar in this current room only": "Canvia el teu avatar només en aquesta sala actual",
"Changes the avatar of the current room": "Canvia l'avatar de la sala actual",
"Changes your display nickname in the current room only": "Canvia el teu àlies de visualització només en la sala actual",
"Changes your display nickname in the current room only": "Canvia el teu àlies a mostrar només en la sala actual",
"Double check that your server supports the room version chosen and try again.": "Comprova que el teu servidor és compatible amb la versió de sala que has triat i torna-ho a intentar.",
"You do not have the required permissions to use this command.": "No disposes dels permisos necessaris per utilitzar aquesta ordre.",
"Sends a message as html, without interpreting it as markdown": "Envia un missatge com a html sense interpretar-lo com a markdown",
@ -951,5 +951,7 @@
"Click the button below to confirm adding this email address.": "Fes clic al botó de sota per confirmar l'addició d'aquesta adreça de correu electrònic.",
"Unable to access webcam / microphone": "No s'ha pogut accedir a la càmera web / micròfon",
"Unable to access microphone": "No s'ha pogut accedir al micròfon",
"Explore rooms": "Explora sales"
"Explore rooms": "Explora sales",
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)sno ha fet canvis",
"%(oneUser)smade no changes %(count)s times|other": "%(oneUser)sno ha fet canvis %(count)s cops"
}

View file

@ -3292,5 +3292,11 @@
"See when people join, leave, or are invited to your active room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do vaší aktivní místnosti",
"Kick, ban, or invite people to this room, and make you leave": "Vykopnout, vykázat, pozvat lidi do této místnosti nebo odejít",
"Kick, ban, or invite people to your active room, and make you leave": "Vykopnout, vykázat, pozvat lidi do vaší aktivní místnosti nebo odejít",
"See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti"
"See when people join, leave, or are invited to this room": "Zjistěte, kdy se lidé připojí, odejdou nebo jsou pozváni do této místnosti",
"Currently joining %(count)s rooms|one": "Momentálně se připojuje %(count)s místnost",
"Currently joining %(count)s rooms|other": "Momentálně se připojuje %(count)s místností",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Vyzkoušejte jiná slova nebo zkontrolujte překlepy. Některé výsledky nemusí být viditelné, protože jsou soukromé a potřebujete k nim pozvánku.",
"No results for \"%(query)s\"": "Žádné výsledky pro \"%(query)s\"",
"The user you called is busy.": "Volaný uživatel je zaneprázdněn.",
"User Busy": "Uživatel zaneprázdněn"
}

View file

@ -3343,5 +3343,14 @@
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Dein Feedback hilfst uns, die Spaces zu verbessern. Je genauer, desto besser.",
"If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Durchs Verlassen lädt %(brand)s mit deaktivierten Spaces neu. Danach kannst du Communities und Custom Tags wieder verwenden.",
"sends space invaders": "sendet Space Invaders",
"Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen"
"Sends the given message with a space themed effect": "Sendet die Nachricht mit Raumschiffen",
"Space Autocomplete": "Spaces automatisch vervollständigen",
"Currently joining %(count)s rooms|one": "Betrete %(count)s Raum",
"Currently joining %(count)s rooms|other": "Betrete %(count)s Räume",
"Go to my space": "Zu meinem Space",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Überprüfe auf Tippfehler oder verwende andere Suchbegriffe. Beachte, dass Ergebnisse aus privaten Räumen, in die du nicht eingeladen wurdest, nicht angezeigt werden.",
"See when people join, leave, or are invited to this room": "Anzeigen, wenn Leute eingeladen werden oder den Raum betreten und verlassen",
"The user you called is busy.": "Der angerufene Benutzer ist momentan beschäftigt.",
"User Busy": "Benutzer beschäftigt",
"No results for \"%(query)s\"": "Keine Ergebnisse für \"%(query)s\""
}

View file

@ -905,6 +905,8 @@
"Incoming voice call": "Incoming voice call",
"Incoming video call": "Incoming video call",
"Incoming call": "Incoming call",
"Sound on": "Sound on",
"Silence call": "Silence call",
"Decline": "Decline",
"Accept": "Accept",
"Pause": "Pause",

View file

@ -1591,10 +1591,10 @@
"Room Autocomplete": "Memkompletigo de ĉambroj",
"User Autocomplete": "Memkompletigo de uzantoj",
"Custom (%(level)s)": "Propra (%(level)s)",
"%(senderName)s placed a voice call.": "%(senderName)s metis voĉvokon.",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s metis voĉvokon. (mankas subteno en ĉi tiu foliumilo)",
"%(senderName)s placed a video call.": "%(senderName)s metis vidvokon.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s metis vidvokon. (mankas subteno en ĉi tiu foliumilo)",
"%(senderName)s placed a voice call.": "%(senderName)s ekigis voĉvokon.",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s ekigis voĉvokon. (mankas subteno en ĉi tiu foliumilo)",
"%(senderName)s placed a video call.": "%(senderName)s ekigis vidvokon.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s ekigis vidvokon. (mankas subteno en ĉi tiu foliumilo)",
"Try out new ways to ignore people (experimental)": "Elprovi novajn manierojn malatenti personojn (eksperimente)",
"Match system theme": "Similiĝi la sisteman haŭton",
"My Ban List": "Mia listo de forbaroj",
@ -3320,5 +3320,11 @@
"Reset event store": "Restarigi deponejon de okazoj",
"If you do, please note that none of your messages will be deleted, but the search experience might be degraded for a few moments whilst the index is recreated": "Se vi tamen tion faras, sciu ke neniu el viaj mesaĝoj foriĝos, sed via sperto pri serĉado povas malboniĝi momente, dum la indekso estas refarata",
"You most likely do not want to reset your event index store": "Plej probable, vi ne volas restarigi vian deponejon de indeksoj de okazoj",
"Reset event store?": "Ĉu restarigi deponejon de okazoj?"
"Reset event store?": "Ĉu restarigi deponejon de okazoj?",
"Currently joining %(count)s rooms|one": "Nun aliĝante al %(count)s ĉambro",
"Currently joining %(count)s rooms|other": "Nun aliĝante al %(count)s ĉambroj",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Provu aliajn vortojn aŭ kontorolu, ĉu vi ne tajperaris. Iuj rezultoj eble ne videblos, ĉar ili estas privataj kaj vi bezonus inviton por aliĝi.",
"No results for \"%(query)s\"": "Neniuj rezultoj por «%(query)s»",
"The user you called is busy.": "La uzanto, kiun vi vokis, estas okupata.",
"User Busy": "Uzanto estas okupata"
}

View file

@ -3315,5 +3315,11 @@
"See when people join, leave, or are invited to your active room": "Ver cuando alguien se una, salga o se le invite a tu sala activa",
"Kick, ban, or invite people to this room, and make you leave": "Expulsar, vetar o invitar personas a esta sala, y hacerte salir de ella",
"Kick, ban, or invite people to your active room, and make you leave": "Expulsar, vetar o invitar a gente a tu sala activa, o hacerte salir",
"See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala"
"See when people join, leave, or are invited to this room": "Ver cuando alguien se une, sale o se le invita a la sala",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Prueba con sinónimos o revisa si te has equivocado al escribir. Puede que algunos resultados no sean visibles si son privados y necesites que te inviten para verlos.",
"Currently joining %(count)s rooms|one": "Entrando en %(count)s sala",
"Currently joining %(count)s rooms|other": "Entrando en %(count)s salas",
"No results for \"%(query)s\"": "Ningún resultado para «%(query)s»",
"The user you called is busy.": "La persona a la que has llamado está ocupada.",
"User Busy": "Persona ocupada"
}

View file

@ -3345,5 +3345,14 @@
"Send and receive voice messages": "Saada ja võta vastu häälsõnumeid",
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Sinu tagasiside aitab teha kogukonnakeskuseid paremaks. Mida detailsemalt sa oma arvamust kirjeldad, seda parem.",
"If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Kui sa lahkud, siis käivitame %(brand)s uuesti nii, et kogukonnakeskused ei ole kasutusel. Vana tüüpi kogukonnad ja kohandatud sildid saavad jälle olema kasutusel.",
"Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud"
"Message search initialisation failed": "Sõnumite otsingu alustamine ei õnnestunud",
"sends space invaders": "korraldab ühe pisikese tulnukate vallutusretke",
"Sends the given message with a space themed effect": "Saadab antud sõnumi kosmoseteemalise efektiga",
"Go to my space": "Palun vaata minu kogukonnakeskust",
"User Busy": "Kasutaja on hõivatud",
"The user you called is busy.": "Kasutaja, kellele sa helistasid, on hõivatud.",
"No results for \"%(query)s\"": "Päringule „%(query)s“ pole vastuseid",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Proovi muid otsingusõnu või kontrolli, et neis polnud trükivigu. Kuna mõned otsingutulemused on privaatsed ja sa vajad kutset nende nägemiseks, siis kõiki tulemusi siin ei pruugi näha olla.",
"Currently joining %(count)s rooms|other": "Parasjagu liitun %(count)s jututoaga",
"Currently joining %(count)s rooms|one": "Parasjagu liitun %(count)s jututoaga"
}

View file

@ -1873,7 +1873,7 @@
"This user has not verified all of their sessions.": "Cet utilisateur na pas vérifié toutes ses sessions.",
"You have verified this user. This user has verified all of their sessions.": "Vous avez vérifié cet utilisateur. Cet utilisateur a vérifié toutes ses sessions.",
"Someone is using an unknown session": "Quelquun utilise une session inconnue",
"Mod": "Modo",
"Mod": "Modérateur",
"Your key share request has been sent - please check your other sessions for key share requests.": "Votre demande de partage de clé a été envoyée vérifiez les demandes de partage de clé sur vos autres sessions.",
"Key share requests are sent to your other sessions automatically. If you rejected or dismissed the key share request on your other sessions, click here to request the keys for this session again.": "Les demandes de partage de clé sont envoyées à vos autres sessions automatiquement. Si vous avez rejeté ou ignoré la demande de partage de clé sur vos autres sessions, cliquez ici pour redemander les clés pour cette session.",
"If your other sessions do not have the key for this message you will not be able to decrypt them.": "Si vos autres sessions nont pas la clé pour ce message vous ne pourrez pas le déchiffrer.",
@ -2842,7 +2842,7 @@
"Change the topic of your active room": "Changer le sujet dans le salon actuel",
"Change the topic of this room": "Changer le sujet de ce salon",
"Send stickers into this room": "Envoyer des autocollants dans ce salon",
"Remain on your screen when viewing another room, when running": "Reste sur votre écran quand vous regardez un autre salon lors de lappel",
"Remain on your screen when viewing another room, when running": "Reste sur votre écran lors de lappel quand vous regardez un autre salon",
"Takes the call in the current room off hold": "Reprend lappel en attente dans ce salon",
"Places the call in the current room on hold": "Met lappel dans ce salon en attente",
"Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Ajoute (╯°□°)╯︵ ┻━┻ en préfixe du message",
@ -2861,14 +2861,14 @@
"Send videos as you in this room": "Envoie des vidéos sous votre nom dans ce salon",
"See images posted to this room": "Voir les images publiées dans ce salon",
"See images posted to your active room": "Voir les images publiées dans votre salon actif",
"See messages posted to your active room": "Voir les messages publiés dans votre salon actif",
"See messages posted to your active room": "Voir les messages envoyés dans le salon actuel",
"See messages posted to this room": "Voir les messages publiés dans ce salon",
"Send messages as you in your active room": "Envoie des messages sous votre nom dans votre salon actif",
"Send messages as you in this room": "Envoie des messages sous votre nom dans ce salon",
"The <b>%(capability)s</b> capability": "La capacité <b>%(capability)s</b>",
"See <b>%(eventType)s</b> events posted to your active room": "Voir les événements <b>%(eventType)s</b> publiés dans votre salon actuel",
"Send <b>%(eventType)s</b> events as you in your active room": "Envoie des événements <b>%(eventType)s</b> sous votre nom dans votre salon actuel",
"See <b>%(eventType)s</b> events posted to this room": "Voir les événements <b>%(eventType)s</b> publiés dans ce salon",
"See <b>%(eventType)s</b> events posted to this room": "Voir les événements <b>%(eventType)s</b> envoyés dans ce salon",
"Send <b>%(eventType)s</b> events as you in this room": "Envoie des événements <b>%(eventType)s</b> sous votre nom dans ce salon",
"Send stickers to your active room as you": "Envoie des autocollants sous votre nom dans le salon actuel",
"Continue with %(ssoButtons)s": "Continuer avec %(ssoButtons)s",
@ -2930,7 +2930,7 @@
"Don't miss a reply": "Ne ratez pas une réponse",
"See <b>%(msgtype)s</b> messages posted to your active room": "Voir les messages de type <b>%(msgtype)s</b> publiés dans le salon actuel",
"See <b>%(msgtype)s</b> messages posted to this room": "Voir les messages de type <b>%(msgtype)s</b> publiés dans ce salon",
"Send <b>%(msgtype)s</b> messages as you in this room": "Envoie des messages de type<b>%(msgtype)s</b> sous votre nom dans ce salon",
"Send <b>%(msgtype)s</b> messages as you in this room": "Envoie les messages de type <b>%(msgtype)s</b> sous votre nom dans ce salon",
"Send <b>%(msgtype)s</b> messages as you in your active room": "Envoie des messages de type <b>%(msgtype)s</b> sous votre nom dans votre salon actif",
"See general files posted to your active room": "Voir les fichiers postés dans votre salon actuel",
"See general files posted to this room": "Voir les fichiers postés dans ce salon",
@ -3034,7 +3034,7 @@
"Send text messages as you in this room": "Envoyez des messages textuels sous votre nom dans ce salon",
"See when the name changes in your active room": "Suivre les changements de nom dans le salon actif",
"Change which room, message, or user you're viewing": "Changer le salon, message, ou la personne que vous visualisez",
"Change which room you're viewing": "Changer le salon que vous visualisez",
"Change which room you're viewing": "Changer le salon que vous êtes en train de lire",
"Remain on your screen while running": "Reste sur votre écran pendant lexécution",
"%(senderName)s has updated the widget layout": "%(senderName)s a mis à jour la disposition du widget",
"Converts the DM to a room": "Transforme la conversation privée en salon",
@ -3111,7 +3111,7 @@
"No permissions": "Aucune permission",
"Remove from Space": "Supprimer de lespace",
"Undo": "Annuler",
"Your message wasn't sent because this homeserver has been blocked by it's administrator. Please <a>contact your service administrator</a> to continue using the service.": "Votre message na pas été envoyé car ce serveur daccueil a été banni par son administrateur. Merci de <a>contacter votre administrateur de service</a> pour poursuivre lusage de ce service.",
"Your message wasn't sent because this homeserver has been blocked by it's administrator. Please <a>contact your service administrator</a> to continue using the service.": "Votre message na pas été envoyé car ce serveur daccueil a été bloqué par son administrateur. Merci de <a>contacter votre administrateur de service</a> pour continuer à utiliser le service.",
"Are you sure you want to leave the space '%(spaceName)s'?": "Êtes-vous sûr de vouloir quitter lespace « %(spaceName)s » ?",
"This space is not public. You will not be able to rejoin without an invite.": "Cet espace nest pas public. Vous ne pourrez pas le rejoindre sans invitation.",
"Start audio stream": "Démarrer une diffusion audio",
@ -3352,5 +3352,11 @@
"sends space invaders": "Envoie les Space Invaders",
"Sends the given message with a space themed effect": "Envoyer le message avec un effet lié au thème de lespace",
"See when people join, leave, or are invited to your active room": "Afficher quand des personnes rejoignent, partent, ou sont invités dans votre salon actif",
"Kick, ban, or invite people to your active room, and make you leave": "Expulser, bannir ou inviter des personnes dans votre salon actif et en partir"
"Kick, ban, or invite people to your active room, and make you leave": "Expulser, bannir ou inviter des personnes dans votre salon actif et en partir",
"Currently joining %(count)s rooms|one": "Vous êtes en train de rejoindre %(count)s salon",
"Currently joining %(count)s rooms|other": "Vous êtes en train de rejoindre %(count)s salons",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Essayez d'autres mots ou vérifiez les fautes de frappe. Certains salons peuvent ne pas être visibles car ils sont privés et vous devez être invité pour les rejoindre.",
"No results for \"%(query)s\"": "Aucun résultat pour « %(query)s »",
"The user you called is busy.": "Lutilisateur que vous avez appelé est indisponible.",
"User Busy": "Utilisateur indisponible"
}

View file

@ -3375,5 +3375,11 @@
"See when people join, leave, or are invited to your active room": "Mira cando alguén se une, sae ou é convidada á túa sala activa",
"Kick, ban, or invite people to your active room, and make you leave": "Expulsa, veta ou convida a persoas á túa sala activa, e fai que saias",
"See when people join, leave, or are invited to this room": "Mira cando se une alguén, sae ou é convidada a esta sala",
"Kick, ban, or invite people to this room, and make you leave": "Expulsa, veta, ou convida persoas a esta sala, e fai que saias"
"Kick, ban, or invite people to this room, and make you leave": "Expulsa, veta, ou convida persoas a esta sala, e fai que saias",
"Currently joining %(count)s rooms|one": "Neste intre estás en %(count)s sala",
"Currently joining %(count)s rooms|other": "Neste intre estás en %(count)s salas",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Intentao con outras palabras e fíxate nos erros de escritura. Algúns resultados poderían non ser visibles porque son privados e precisas un convite.",
"No results for \"%(query)s\"": "Sen resultados para \"%(query)s\"",
"The user you called is busy.": "A persoa á que chamas está ocupada.",
"User Busy": "Usuaria ocupada"
}

View file

@ -5,5 +5,205 @@
"The platform you're on": "Platforma na kojoj se nalazite",
"The version of %(brand)s": "Verzija %(brand)s",
"Your language of choice": "Izabrani jezik",
"Dismiss": "Odbaci"
"Dismiss": "Odbaci",
"France": "Francuska",
"Finland": "Finska",
"Fiji": "Fiji",
"Faroe Islands": "Farski otoci",
"Falkland Islands": "Falklandski otoci",
"Ethiopia": "Etiopija",
"Estonia": "Estonija",
"Eritrea": "Eritreja",
"Equatorial Guinea": "Ekvatorska Gvineja",
"El Salvador": "El Salvador",
"Egypt": "Egipat",
"Ecuador": "Ekvador",
"Dominican Republic": "Dominikanska Republika",
"Dominica": "Dominika",
"Djibouti": "Džibuti",
"Denmark": "Danska",
"Côte dIvoire": "Obala Bjelokosti",
"Czech Republic": "Češka",
"Cyprus": "Cipar",
"Curaçao": "Curaçao",
"Cuba": "Kuba",
"Croatia": "Hrvatska",
"Costa Rica": "Kostarika",
"Cook Islands": "Cookovo Otočje",
"Congo - Kinshasa": "Kongo - Kinshasa",
"Congo - Brazzaville": "Republika Kongo",
"Comoros": "Komori",
"Colombia": "Kolumbija",
"Cocos (Keeling) Islands": "Kokosovi (Keeling) otoci",
"Christmas Island": "Uskršnji otoci",
"China": "Kina",
"Chile": "Čile",
"Chad": "Čad",
"Central African Republic": "Srednjoafrička Republika",
"Cayman Islands": "Kajmanski otoci",
"Caribbean Netherlands": "Karipska Nizozemska",
"Cape Verde": "Zelenortski Otoci",
"Canada": "Kanada",
"Cameroon": "Kamerun",
"Cambodia": "Kambodža",
"Burundi": "Burundi",
"Burkina Faso": "Burkina Faso",
"Bulgaria": "Bugarska",
"Brunei": "Brunej",
"British Virgin Islands": "Britanski djevičanski otoci",
"British Indian Ocean Territory": "Britanski teritorij Indijskog oceana",
"Brazil": "Brazil",
"Bouvet Island": "Otok Bouvet",
"Botswana": "Bocvana",
"Bosnia": "Bosna i Hercegovina",
"Bolivia": "Bolivija",
"Bhutan": "Butan",
"Bermuda": "Bermuda",
"Benin": "Benin",
"Belize": "Belize",
"Belgium": "Belgija",
"Belarus": "Bjelorusija",
"Barbados": "Barbados",
"Bangladesh": "Bangladeš",
"Bahrain": "Bahrein",
"Bahamas": "Bahami",
"Azerbaijan": "Azerbejdžan",
"Austria": "Austrija",
"Australia": "Australija",
"Aruba": "Aruba",
"Armenia": "Armenija",
"Argentina": "Argentina",
"Antigua & Barbuda": "Antigva i Barbuda",
"Antarctica": "Antartika",
"Anguilla": "Angvila",
"Angola": "Angola",
"Andorra": "Andora",
"American Samoa": "Američka Samoa",
"Algeria": "Alžir",
"Albania": "Albanija",
"Åland Islands": "Alandski otoci",
"Afghanistan": "Afganistan",
"United States": "Sjedinjene Države",
"United Kingdom": "Ujedinjeno Kraljevstvo",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Čini se da Vaša email adresa nije povezana s Matrix ID-om na ovom kućnom poslužitelju.",
"This email address was not found": "Ova email adresa nije pronađena",
"Unable to enable Notifications": "Omogućavanje notifikacija nije uspjelo",
"%(brand)s was not given permission to send notifications - please try again": "%(brand)s nema dopuštenje slati Vam notifikacije - molimo pokušajte ponovo",
"%(brand)s does not have permission to send you notifications - please check your browser settings": "%(brand)s nema dopuštenje slati Vam notifikacije - molimo provjerite postavke pretraživača",
"%(name)s is requesting verification": "%(name)s traži potvrdu",
"Your homeserver rejected your log in attempt. This could be due to things just taking too long. Please try again. If this continues, please contact your homeserver administrator.": "Vaš je kućni poslužitelj odbio vaš pokušaj prijave. Razlog je možda da je jednostavno sve predugo trajalo. Molimo pokušajte ponovno. Ako se ovo nastavi, obratite se administratoru kućnog poslužitelja.",
"Your homeserver was unreachable and was not able to log you in. Please try again. If this continues, please contact your homeserver administrator.": "Vaš kućni poslužitelj nije bio dostupan i nije vas mogao prijaviti. Pokušajte ponovo. Ako se ovo nastavi, obratite se administratoru kućnog poslužitelja.",
"Try again": "Pokušaj ponovo",
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Tražili smo od preglednika da zapamti koji kućni poslužitelj koristite za prijavu, ali ga je Vaš preglednik nažalost zaboravio. Idite na stranicu za prijavu i pokušajte ponovo.",
"We couldn't log you in": "Nismo Vas mogli ulogirati",
"Trust": "Vjeruj",
"Only continue if you trust the owner of the server.": "Nastavite samo ako vjerujete vlasniku poslužitelja.",
"This action requires accessing the default identity server <server /> to validate an email address or phone number, but the server does not have any terms of service.": "Ova radnja zahtijeva pristup zadanom poslužitelju identiteta <server /> radi provjere adrese e-pošte ili telefonskog broja, no poslužitelj nema nikakve uvjete usluge.",
"Identity server has no terms of service": "Poslužitelj identiteta nema uvjete usluge",
"Unnamed Room": "Neimenovana soba",
"Failed to add the following rooms to %(groupId)s:": "Neuspješno dodavanje sljedećih soba u %(groupId)s:",
"Failed to invite users to %(groupId)s": "Neuspješno dodavanje korisnika u %(groupId)s",
"Failed to invite users to community": "Dodavanje korisnika u zajednicu nije uspjelo",
"Failed to invite the following users to %(groupId)s:": "Neuspješno dodavanje sljedećih korisnika u %(groupId)s:",
"Add to community": "Dodaj u zajednicu",
"Room name or address": "Ime ili adresa sobe",
"Add rooms to the community": "Dodaj sobe zajednici",
"Show these rooms to non-members on the community page and room list?": "Prikaži ove sobe osobama koje nisu članovi na stranici zajednice i popisu soba?",
"Which rooms would you like to add to this community?": "Koju sobu biste željeli dodati u ovu zajednicu?",
"Invite to Community": "Pozovi u zajednicu",
"Name or Matrix ID": "Ime ili Matrix ID",
"Invite new community members": "Pozovite nove članove zajednice",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Upozorenje: svaka osoba koju dodate u zajednicu bit će javno vidljiva svima koji znaju ID zajednice",
"Who would you like to add to this community?": "Koga biste željeli dodati u ovu zajednicu?",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s, %(time)s",
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s": "%(weekDayName)s, %(day)s. %(monthName)s %(fullYear)s",
"%(weekDayName)s, %(monthName)s %(day)s %(time)s": "%(weekDayName)s, %(day)s. %(monthName)s, %(time)s",
"%(weekDayName)s %(time)s": "%(weekDayName)s %(time)s",
"AM": "Prijepodne",
"PM": "Poslijepodne",
"Dec": "Pro",
"Nov": "Stu",
"Oct": "Lis",
"Sep": "Ruj",
"Aug": "Kol",
"Jul": "Srp",
"Jun": "Lip",
"May": "Svi",
"Apr": "Tra",
"Mar": "Ožu",
"Feb": "Velj",
"Jan": "Sij",
"Sat": "Sub",
"Fri": "Pet",
"Thu": "Čet",
"Wed": "Sri",
"Tue": "Uto",
"Mon": "Pon",
"Sun": "Ned",
"Failure to create room": "Stvaranje sobe neuspješno",
"The server does not support the room version specified.": "Poslužitelj ne podržava navedenu verziju sobe.",
"Server may be unavailable, overloaded, or you hit a bug.": "Poslužitelj je možda nedostupan, preopterećen, ili ste pronašli grešku u aplikaciji.",
"Upload Failed": "Prijenos neuspješan",
"The file '%(fileName)s' exceeds this homeserver's size limit for uploads": "Datoteka '%(fileName)s' premašuje maksimalnu veličinu ovog kućnog poslužitelja za prijenose",
"The file '%(fileName)s' failed to upload.": "Prijenos datoteke '%(fileName)s' nije uspio.",
"Continue": "Nastavi",
"At this time it is not possible to reply with a file. Would you like to upload this file without replying?": "Trenutno nije moguće odgovoriti datotekom. Želite li prenijeti ovu datoteku bez odgovora?",
"Replying With Files": "Odgovaranje datotekama",
"This will end the conference for everyone. Continue?": "To će prekinuti konferenciju za sve. Nastaviti?",
"End conference": "Završi konferenciju",
"You do not have permission to start a conference call in this room": "Nemate dopuštenje uspostaviti konferencijski poziv u ovoj sobi",
"Permission Required": "Potrebno dopuštenje",
"A call is currently being placed!": "Poziv se upravo uspostavlja!",
"Call in Progress": "Poziv u tijeku",
"You cannot place a call with yourself.": "Ne možete uspostaviti poziv sami sa sobom.",
"You're already in a call with this person.": "Već ste u pozivu sa tom osobom.",
"Already in call": "Već u pozivu",
"You've reached the maximum number of simultaneous calls.": "Dosegli ste maksimalan broj istodobnih poziva.",
"Too Many Calls": "Previše poziva",
"You cannot place VoIP calls in this browser.": "Ne možete uspostaviti VoIP pozive u ovom pretraživaču.",
"VoIP is unsupported": "VoIP nije podržan",
"Unable to capture screen": "Nije moguće snimanje zaslona",
"No other application is using the webcam": "Da ni jedna druga aplikacija već ne koristi web kameru",
"Permission is granted to use the webcam": "Jeli dopušteno korištenje web kamere",
"A microphone and webcam are plugged in and set up correctly": "Jesu li mikrofon i web kamera priključeni i pravilno postavljeni",
"Unable to access webcam / microphone": "Nije moguće pristupiti web kameri / mikrofonu",
"Call failed because webcam or microphone could not be accessed. Check that:": "Poziv nije uspio jer nije bilo moguće pristupiti web kameri ili mikrofonu. Provjerite:",
"Call failed because microphone could not be accessed. Check that a microphone is plugged in and set up correctly.": "Poziv nije uspio jer nije bilo moguće pristupiti mikrofonu. Provjerite je li mikrofon priključen i ispravno postavljen.",
"Unable to access microphone": "Nije moguće pristupiti mikrofonu",
"OK": "OK",
"Try using turn.matrix.org": "Pokušajte koristiti turn.matrix.org",
"Alternatively, you can try to use the public server at <code>turn.matrix.org</code>, but this will not be as reliable, and it will share your IP address with that server. You can also manage this in Settings.": "Alternativno, možete pokušati koristiti javni poslužitelj na <code>turn.matrix.org</code>, no to bi moglo biti manje pouzdano i Vaša IP adresa će biti podijeljena s tim poslužiteljem. Time također možete upravljati u Postavkama.",
"Please ask the administrator of your homeserver (<code>%(homeserverDomain)s</code>) to configure a TURN server in order for calls to work reliably.": "Zamolite administratora Vašeg kućnog poslužitelja (<code>%(homeserverDomain)s</code>) da konfigurira TURN poslužitelj kako bi pozivi mogli pouzdano funkcionirati.",
"Call failed due to misconfigured server": "Poziv neuspješan radi pogrešno konfiguriranog poslužitelja",
"The call was answered on another device.": "Na poziv je odgovoreno sa drugog uređaja.",
"Answered Elsewhere": "Odgovoreno je drugdje",
"The call could not be established": "Poziv se nije mogao uspostaviti",
"The remote side failed to pick up": "Sugovornik nije odgovorio na poziv",
"The user you called is busy.": "Pozvani korisnik je zauzet.",
"User Busy": "Korisnik zauzet",
"The other party declined the call.": "Sugovornik je odbio poziv.",
"Call Declined": "Poziv odbijen",
"Call Failed": "Poziv neuspješan",
"Unable to load! Check your network connectivity and try again.": "Učitavanje nije moguće! Provjerite mrežnu povezanost i pokušajte ponovo.",
"Error": "Geška",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Gdje ova stranica uključuje identificirajuće podatke, poput ID-a sobe, korisnika ili grupe, ti se podaci uklanjaju prije slanja na poslužitelj.",
"The information being sent to us to help make %(brand)s better includes:": "Podaci koji nam se šalju radi poboljšanja %(brand)s uključuju:",
"Analytics": "Analitika",
"Your device resolution": "Razlučivost vašeg uređaja",
"Your user agent": "Vaš korisnički agent",
"e.g. <CurrentPageURL>": "npr. <CurrentPageURL>",
"Every page you use in the app": "Svaka stranica koju upotrebljavate u aplikaciji",
"Whether you're using %(brand)s on a device where touch is the primary input mechanism": "Bez obzira upotrebljavate li %(brand)s na uređaju na kojem je dodir primarni mehanizam unosa",
"Confirm adding this phone number by using Single Sign On to prove your identity.": "Potvrdite dodavanje ovog telefonskog broja koristeći jedinstvenu prijavu (SSO) da biste dokazali Vaš identitet.",
"Confirm adding this email address by using Single Sign On to prove your identity.": "Potvrdite dodavanje ove email adrese koristeći jedinstvenu prijavu (SSO) da biste dokazali Vaš identitet.",
"Single Sign On": "Jedinstvena prijava (SSO)",
"Use Single Sign On to continue": "Koristite jedinstvenu prijavu (SSO) za nastavak",
"Whether or not you're logged in (we don't record your username)": "Bez obzira jeste li ulogirani ili ne (ne snimamo vaše korisničko ime)",
"Add Phone Number": "Dodaj telefonski broj",
"Click the button below to confirm adding this phone number.": "Kliknite gumb ispod da biste potvrdili dodavanje ovog telefonskog broja.",
"Confirm adding phone number": "Potvrdite dodavanje telefonskog broja",
"Add Email Address": "Dodaj email adresu",
"Confirm": "Potvrdi",
"Click the button below to confirm adding this email address.": "Kliknite gumb ispod da biste potvrdili dodavanje ove email adrese.",
"Confirm adding email": "Potvrdite dodavanje email adrese"
}

View file

@ -26,12 +26,12 @@
"Admin Tools": "Admin. Eszközök",
"No Microphones detected": "Nem található mikrofon",
"No Webcams detected": "Nem található webkamera",
"No media permissions": "Nincs media jogosultság",
"No media permissions": "Nincs média jogosultság",
"You may need to manually permit %(brand)s to access your microphone/webcam": "Lehet hogy kézileg kell engedélyeznie a %(brand)snak, hogy hozzáférjen a mikrofonjához és webkamerájához",
"Default Device": "Alapértelmezett eszköz",
"Microphone": "Mikrofon",
"Camera": "Kamera",
"Advanced": "Haladó",
"Advanced": "Speciális",
"Always show message timestamps": "Üzenet időbélyeg folyamatos megjelenítése",
"Authentication": "Azonosítás",
"Failed to change password. Is your password correct?": "Nem sikerült megváltoztatni a jelszót. Helyesen írtad be a jelszavadat?",
@ -66,7 +66,7 @@
"%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s megváltoztatta a hozzáférési szintjét erre: %(powerLevelDiffText)s.",
"%(senderDisplayName)s changed the room name to %(roomName)s.": "%(senderDisplayName)s megváltoztatta a szoba nevét erre: %(roomName)s.",
"%(senderDisplayName)s removed the room name.": "%(senderDisplayName)s törölte a szoba nevét.",
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s megváltoztatta a témát erre \"%(topic)s\".",
"%(senderDisplayName)s changed the topic to \"%(topic)s\".": "%(senderDisplayName)s megváltoztatta a témát erre: „%(topic)s”.",
"Changes your display nickname": "Megváltoztatja a becenevedet",
"Click here to fix": "A javításhoz kattints ide",
"Click to mute audio": "Hang némításához kattints ide",
@ -116,7 +116,7 @@
"Failed to set display name": "Megjelenítési nevet nem sikerült beállítani",
"Failed to unban": "Kizárás visszavonása sikertelen",
"Failed to upload profile picture!": "Profil kép feltöltése sikertelen!",
"Failed to verify email address: make sure you clicked the link in the email": "E-mail cím ellenőrzése sikertelen: ellenőrizze, hogy az e-mailben lévő hivatkozásra kattintott-e",
"Failed to verify email address: make sure you clicked the link in the email": "Az e-mail-cím ellenőrzése sikertelen: ellenőrizze, hogy az e-mailben lévő hivatkozásra kattintott-e",
"Failure to create room": "Szoba létrehozása sikertelen",
"Favourites": "Kedvencek",
"Fill screen": "Képernyő kitöltése",
@ -230,7 +230,7 @@
"Submit": "Elküld",
"Success": "Sikeres",
"The phone number entered looks invalid": "A megadott telefonszám érvénytelennek tűnik",
"This email address is already in use": "Ez az e-mail cím már használatban van",
"This email address is already in use": "Ez az e-mail-cím már használatban van",
"This email address was not found": "Az e-mail cím nem található",
"The email address linked to your account must be entered.": "A fiókodhoz kötött e-mail címet add meg.",
"The remote side failed to pick up": "A hívott fél nem vette fel",
@ -1030,7 +1030,7 @@
"Room list": "Szoba lista",
"Timeline": "Idővonal",
"Autocomplete delay (ms)": "Automatikus kiegészítés késleltetése (ms)",
"Roles & Permissions": "Szerepek & Jogosultságok",
"Roles & Permissions": "Szerepek és jogosultságok",
"Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "A üzenetek olvashatóságának változtatása csak az új üzenetekre lesz érvényes. A régi üzenetek láthatósága nem fog változni.",
"Security & Privacy": "Biztonság és adatvédelem",
"Encryption": "Titkosítás",
@ -1373,7 +1373,7 @@
"Message edits": "Üzenet szerkesztések",
"Upgrading this room requires closing down the current instance of the room and creating a new room in its place. To give room members the best possible experience, we will:": "A szoba frissítéséhez be kell zárnod ezt a szobát és egy újat kell nyitnod e helyett. A szoba tagjainak a legjobb felhasználói élmény nyújtásához az alábbit fogjuk tenni:",
"Loading room preview": "Szoba előnézetének a betöltése",
"Show all": "Mindet mutat",
"Show all": "Mind megjelenítése",
"%(senderName)s made no change.": "%(senderName)s nem változtatott semmit.",
"%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)s %(count)s alkalommal nem változtattak semmit",
"%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)s nem változtattak semmit",
@ -1531,7 +1531,7 @@
"%(count)s unread messages including mentions.|other": "%(count)s olvasatlan üzenet megemlítéssel.",
"%(count)s unread messages.|other": "%(count)s olvasatlan üzenet.",
"Unread mentions.": "Olvasatlan megemlítés.",
"Show image": "Képek megmutatása",
"Show image": "Kép megjelenítése",
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.": "Ahhoz hogy a hibát megvizsgálhassuk kérlek <newIssueLink>készíts egy új hibajegyet</newIssueLink> a GitHubon.",
"To continue you need to accept the terms of this service.": "A folytatáshoz el kell fogadnod a felhasználási feltételeket.",
"Document": "Dokumentum",
@ -1545,7 +1545,7 @@
"Remove %(count)s messages|one": "1 üzenet törlése",
"Your email address hasn't been verified yet": "Az e-mail-címe még nincs ellenőrizve",
"Click the link in the email you received to verify and then click continue again.": "Ellenőrzéshez kattints a linkre az e-mailben amit kaptál és itt kattints a folytatásra újra.",
"Add Email Address": "E-mail cím hozzáadása",
"Add Email Address": "E-mail-cím hozzáadása",
"Add Phone Number": "Telefonszám hozzáadása",
"%(creator)s created and configured the room.": "%(creator)s elkészítette és beállította a szobát.",
"You should <b>remove your personal data</b> from identity server <idserver /> before disconnecting. Unfortunately, identity server <idserver /> is currently offline or cannot be reached.": "Először <b>töröld a személyes adatokat</b> az azonosítási szerverről (<idserver />) mielőtt lecsatlakozol. Sajnos az azonosítási szerver (<idserver />) jelenleg elérhetetlen.",
@ -1622,7 +1622,7 @@
"Subscribing to a ban list will cause you to join it!": "A feliratkozás egy tiltó listára azzal jár, hogy csatlakozol hozzá!",
"If this isn't what you want, please use a different tool to ignore users.": "Ha nem ez az amit szeretnél, kérlek használj más eszközt a felhasználók figyelmen kívül hagyásához.",
"Subscribe": "Feliratkozás",
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>": "Ezt a felhasználót figyelmen kívül hagyod, így az üzenetei el lesznek rejtve. <a>Mindenképpen megmutat.</a>",
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>": "Ezt a felhasználót figyelmen kívül hagyod, így az üzenetei el lesznek rejtve. <a>Megjelenítés mindenképpen.</a>",
"Custom (%(level)s)": "Egyéni (%(level)s)",
"Trusted": "Megbízható",
"Not trusted": "Megbízhatatlan",
@ -1733,7 +1733,7 @@
"Country Dropdown": "Ország lenyíló menü",
"The message you are trying to send is too large.": "Túl nagy képet próbálsz elküldeni.",
"Help": "Súgó",
"Show more": "Mutass többet",
"Show more": "Több megjelenítése",
"Recent Conversations": "Legújabb Beszélgetések",
"Direct Messages": "Közvetlen Beszélgetések",
"Go": "Menj",
@ -1795,7 +1795,7 @@
"This bridge was provisioned by <user />.": "Ezt a hidat az alábbi felhasználó készítette: <user />.",
"Workspace: %(networkName)s": "Munkahely: %(networkName)s",
"Channel: %(channelName)s": "Csatorna: %(channelName)s",
"Show less": "Kevesebbet mutat",
"Show less": "Kevesebb megjelenítése",
"Securely cache encrypted messages locally for them to appear in search results, using ": "A titkosított üzenetek kereséséhez azokat biztonságos módon helyileg kell tárolnod, felhasználva: ",
" to store messages from ": " üzenetek tárolásához ",
"rooms.": "szobából.",
@ -2109,7 +2109,7 @@
"Server did not return valid authentication information.": "A szerver semmilyen érvényes azonosítási információt sem küldött vissza.",
"There was a problem communicating with the server. Please try again.": "A szerverrel való kommunikációval probléma történt. Kérlek próbáld újra.",
"Sign in with SSO": "Belépés SSO-val",
"Welcome to %(appName)s": "Üdvözöl az %(appName)s",
"Welcome to %(appName)s": "Üdvözöl a(z) %(appName)s",
"Liberate your communication": "Kommunikálj szabadon",
"Send a Direct Message": "Közvetlen üzenet küldése",
"Explore Public Rooms": "Nyilvános szobák felfedezése",
@ -2237,7 +2237,7 @@
"Sort by": "Rendezés",
"Unread rooms": "Olvasatlan szobák",
"Always show first": "Mindig az elsőt mutatja",
"Show": "Mutat",
"Show": "Megjelenítés",
"Message preview": "Üzenet előnézet",
"List options": "Lista beállításai",
"Show %(count)s more|other": "Még %(count)s megjelenítése",
@ -2945,7 +2945,7 @@
"Learn more": "Tudj meg többet",
"Matrix.org is the biggest public homeserver in the world, so its a good place for many.": "A matrix.org a legnagyobb nyilvános Matrix szerver a világon, és sok felhasználónak megfelelő választás.",
"About homeservers": "A Matrix szerverekről",
"Use your preferred Matrix homeserver if you have one, or host your own.": "Add meg az általad választott Matrix szerver címét, ha van ilyen, vagy üzemeltess egy sajátot!",
"Use your preferred Matrix homeserver if you have one, or host your own.": "Add meg az általad választott Matrix szerver címét, ha van ilyen, vagy üzemeltess egy sajátot.",
"Other homeserver": "Másik Matrix szerver",
"Host account on": "Fiók létrehozása itt:",
"We call the places where you can host your account homeservers.": "Matrix szervereknek nevezzük azokat a helyeket, ahol fiókot lehet létrehozni.",
@ -3107,7 +3107,7 @@
"Room name": "Szoba neve",
"Support": "Támogatás",
"Random": "Véletlen",
"Welcome to <name/>": "Üdvözlöm itt: <name/>",
"Welcome to <name/>": "Üdvözöl a(z) <name/>",
"Your private space <name/>": "Privát tere: <name/>",
"Your public space <name/>": "Nyilvános tere: <name/>",
"You have been invited to <name/>": "Meghívták ide: <name/>",
@ -3370,5 +3370,11 @@
"See when people join, leave, or are invited to your active room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése az aktív szobájában",
"Kick, ban, or invite people to your active room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket az aktív szobába és, hogy ön elhagyja a szobát",
"See when people join, leave, or are invited to this room": "Emberek belépésének, távozásának vagy meghívásának a megjelenítése ebben a szobában",
"Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát"
"Kick, ban, or invite people to this room, and make you leave": "Kirúgni, kitiltani vagy meghívni embereket ebbe a szobába és, hogy ön elhagyja a szobát",
"Currently joining %(count)s rooms|one": "%(count)s szobába lép be",
"Currently joining %(count)s rooms|other": "%(count)s szobába lép be",
"No results for \"%(query)s\"": "Nincs találat ehhez: %(query)s",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Próbáljon ki más szavakat vagy keressen elgépelést. Néhány találat azért nem látszik, mert privát és meghívóra van szüksége, hogy csatlakozhasson.",
"The user you called is busy.": "A hívott felhasználó foglalt.",
"User Busy": "Felhasználó foglalt"
}

View file

@ -720,5 +720,13 @@
"%(count)s messages deleted.|one": "%(count)s skilaboð eytt.",
"%(count)s messages deleted.|other": "%(count)s skilaboðum eytt.",
"Message deleted on %(date)s": "Skilaboð eytt á %(date)s",
"Message edits": "Skilaboðs breytingar"
"Message edits": "Skilaboðs breytingar",
"List options": "Lista valkosti",
"Create a Group Chat": "Búa Til Hópspjall",
"Explore Public Rooms": "Kanna Almenningsherbergi",
"Explore public rooms": "Kanna almenningsherbergi",
"Explore all public rooms": "Kanna öll almenningsherbergi",
"Liberate your communication": "Frelsaðu samskipti þín",
"Welcome to <name/>": "Velkomin til <name/>",
"Welcome to %(appName)s": "Velkomin til %(appName)s"
}

View file

@ -3375,5 +3375,11 @@
"Kick, ban, or invite people to your active room, and make you leave": "Buttare fuori, bandire o invitare persone nella tua stanza attiva e farti uscire",
"See when people join, leave, or are invited to this room": "Vedere quando le persone entrano, escono o sono invitate in questa stanza",
"Kick, ban, or invite people to this room, and make you leave": "Buttare fuori, bandire o invitare persone in questa stanza e farti uscire",
"See when people join, leave, or are invited to your active room": "Vedere quando le persone entrano, escono o sono invitate nella tua stanza attiva"
"See when people join, leave, or are invited to your active room": "Vedere quando le persone entrano, escono o sono invitate nella tua stanza attiva",
"Currently joining %(count)s rooms|one": "Stai entrando in %(count)s stanza",
"Currently joining %(count)s rooms|other": "Stai entrando in %(count)s stanze",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Prova parole diverse o controlla errori di battitura. Alcuni risultati potrebbero non essere visibili dato che sono privati e ti servirebbe un invito per unirti.",
"No results for \"%(query)s\"": "Nessun risultato per \"%(query)s\"",
"The user you called is busy.": "L'utente che hai chiamato è occupato.",
"User Busy": "Utente occupato"
}

View file

@ -3261,5 +3261,11 @@
"See when people join, leave, or are invited to your active room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd in uw actieve gesprek",
"Kick, ban, or invite people to your active room, and make you leave": "Verwijder, verban of nodig personen uit voor uw actieve gesprek en uzelf laten vertrekken",
"See when people join, leave, or are invited to this room": "Zie wanneer personen deelnemen, vertrekken of worden uitgenodigd voor dit gesprek",
"Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken"
"Kick, ban, or invite people to this room, and make you leave": "Verwijder, verban of verwijder personen uit dit gesprek en uzelf laten vertrekken",
"Currently joining %(count)s rooms|one": "Momenteel aan het toetreden tot %(count)s gesprek",
"Currently joining %(count)s rooms|other": "Momenteel aan het toetreden tot %(count)s gesprekken",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Probeer andere woorden of controleer op typefouten. Sommige resultaten zijn mogelijk niet zichtbaar omdat ze privé zijn of u een uitnodiging nodig heeft om deel te nemen.",
"No results for \"%(query)s\"": "Geen resultaten voor \"%(query)s\"",
"The user you called is busy.": "De gebruiker die u belde is bezet.",
"User Busy": "Gebruiker Bezet"
}

View file

@ -2271,5 +2271,86 @@
"We asked the browser to remember which homeserver you use to let you sign in, but unfortunately your browser has forgotten it. Go to the sign in page and try again.": "Poprosiliśmy przeglądarkę o zapamiętanie, z którego serwera głównego korzystasz, aby umożliwić Ci logowanie, ale niestety Twoja przeglądarka o tym zapomniała. Przejdź do strony logowania i spróbuj ponownie.",
"We couldn't log you in": "Nie mogliśmy Cię zalogować",
"You're already in a call with this person.": "Prowadzisz już rozmowę z tą osobą.",
"Already in call": "Już dzwoni"
"Already in call": "Już dzwoni",
"Never send encrypted messages to unverified sessions in this room from this session": "Nigdy nie wysyłaj zaszyfrowanych wiadomości do niezweryfikowanych sesji z tej sesji w tym pokoju",
"Use the <a>Desktop app</a> to see all encrypted files": "Użyj <a>aplikacji desktopowej</a>, aby zobaczyć wszystkie szyfrowane pliki",
"Youre all caught up": "Jesteś na bieżąco",
"Verification requested": "Zażądano weryfikacji",
"You have no visible notifications.": "Nie masz widocznych powiadomień.",
"Connecting": "Łączenie",
"Your Security Key has been <b>copied to your clipboard</b>, paste it to:": "Twój klucz bezpieczeństwa został <b>skopiowany do schowka</b>, wklej go:",
"Your Security Key": "Twój klucz bezpieczeństwa",
"Create key backup": "Utwórz kopię zapasową klucza",
"Generate a Security Key": "Wygeneruj klucz bezpieczeństwa",
"Safeguard against losing access to encrypted messages & data by backing up encryption keys on your server.": "Zabezpiecz się przed utratą dostępu do szyfrowanych wiadomości i danych, tworząc kopię zapasową kluczy szyfrowania na naszym serwerze.",
"Safeguard against losing access to encrypted messages & data": "Zabezpiecz się przed utratą dostępu do szyfrowanych wiadomości i danych",
"Access Token": "Token dostępu",
"Your access token gives full access to your account. Do not share it with anyone.": "Twój token dostępu daje pełen dostęp do Twojego konta. Nie dziel się nim z nikim.",
"You can leave the beta any time from settings or tapping on a beta badge, like the one above.": "Możesz opuścić betę w każdej chwili z ustawień lub klikając plakietę Beta, taką jak powyższa.",
"Beta available for web, desktop and Android. Some features may be unavailable on your homeserver.": "Beta dostępna w przeglądarce, na komputerach i na Androidzie. Niektóre funkcje mogą nie być dostępne na Twoim serwerze.",
"Beta available for web, desktop and Android. Thank you for trying the beta.": "Beta dostępna w przeglądarce, na komputerach i na Androidzie. Dziękujemy za wypróbowanie bety.",
"Avatar": "Awatar",
"Leave the beta": "Opuść betę",
"Beta": "Beta",
"Tap for more info": "Naciśnij aby dowiedzieć się więcej",
"Move right": "Przenieś w prawo",
"Move left": "Przenieś w lewo",
"Join the beta": "Dołącz do bety",
"To join %(spaceName)s, turn on the <a>Spaces beta</a>": "Aby dołączyć do %(spaceName)s, włącz <a>betę Przestrzeni</a>",
"No results found": "Nie znaleziono wyników",
"Create room": "Utwórz pokój",
"Removing...": "Usuwanie…",
"Your server does not support showing space hierarchies.": "Twój serwer nie obsługuje wyświetlania hierarchii przestrzeni.",
"You can select all or individual messages to retry or delete": "Możesz zaznaczyć wszystkie lub wybrane wiadomości aby spróbować ponownie lub usunąć je",
"Sending": "Wysyłanie",
"Delete all": "Usuń wszystkie",
"Some of your messages have not been sent": "Niektóre z Twoich wiadomości nie zostały wysłane",
"Filter rooms and people": "Filtruj pokoje i ludzi",
"No results for \"%(query)s\"": "Brak wyników dla „%(query)s”",
"Explore rooms in %(communityName)s": "Eksploruj pokoje w %(communityName)s",
"Retry all": "Spróbuj ponownie wszystkie",
"Suggested": "Polecany",
"This room is suggested as a good one to join": "Ten pokój jest polecany jako dobry do dołączenia",
"%(count)s rooms|one": "%(count)s pokój",
"%(count)s rooms|other": "%(count)s pokoi",
"%(count)s members|one": "%(count)s członek",
"%(count)s members|other": "%(count)s członkowie",
"You don't have permission": "Nie masz uprawnień",
"Failed to remove some rooms. Try again later": "Nie udało się usunąć niektórych pokoi. Spróbuj ponownie później",
"Select a room below first": "Najpierw wybierz poniższy pokój",
"%(count)s rooms and 1 space|one": "%(count)s pokój i jedna przestrzeń",
"%(count)s rooms and 1 space|other": "%(count)s pokoi i jedna przestrzeń",
"To view %(spaceName)s, turn on the <a>Spaces beta</a>": "Aby wyświetlić %(spaceName)s, włącz <a>betę Przestrzeni</a>",
"Spaces are a beta feature.": "Przestrzenie są funkcją beta.",
"%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s pokój i %(numSpaces)s przestrzeni",
"%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s pokoi i %(numSpaces)s przestrzeni",
"Filter all spaces": "Filtruj wszystkie przestrzenie",
"Communities are changing to Spaces": "Społeczności zmieniają się na Przestrzenie",
"Spaces is a beta feature": "Przestrzenie są funkcją beta",
"You can add existing spaces to a space.": "Możesz dodać istniejące przestrzenie do przestrzeni.",
"Filter your rooms and spaces": "Filtruj pokoje i przestrzenie",
"%(count)s results in all spaces|one": "%(count)s wynik we wszystkich przestrzeniach",
"%(count)s results in all spaces|other": "%(count)s wyników we wszystkich przestrzeniach",
"Spaces are a new way to group rooms and people. To join an existing space you'll need an invite.": "Przestrzenie to nowy sposób na grupowanie pokoi i ludzi. Aby dołączyć do istniejącej przestrzeni, potrzebujesz zaproszenia.",
"Your feedback will help make spaces better. The more detail you can go into, the better.": "Twoje opinie pomogą uczynić Przestrzenie lepszymi. Im bardziej szczegółowo je opiszesz, tym lepiej.",
"%(brand)s will reload with Spaces enabled. Communities and custom tags will be hidden.": "%(brand)s odświeży się z włączonymi Przestrzeniami. Społeczności i niestandardowe tagi zostaną ukryte.",
"If you leave, %(brand)s will reload with Spaces disabled. Communities and custom tags will be visible again.": "Jeżeli opuścisz, %(brand)s odświeży się z wyłączonymi Przestrzeniami. Społeczności i niestandardowe tagi będą z powrotem widoczne.",
"Spaces are a new way to group rooms and people.": "Przestrzenie to nowy sposób grupowania pokoi i ludzi.",
"Spaces": "Przestrzenie",
"Feeling experimental?": "Chcesz eksperymentować?",
"Feeling experimental? Labs are the best way to get things early, test out new features and help shape them before they actually launch. <a>Learn more</a>.": "Chcesz eksperymentować? Laboratoria to najlepszy sposób na uzyskanie nowości wcześniej, przetestowanie nowych funkcji i pomoc w kształtowaniu ich zanim będą ogólnodostępne. <a>Dowiedz się więcej</a>.",
"We'll store an encrypted copy of your keys on our server. Secure your backup with a Security Phrase.": "Będziemy przechowywać zaszyfrowaną kopię Twoich kluczy na naszym serwerze. Zabezpiecz swoją kopię zapasową frazą bezpieczeństwa.",
"Secure Backup": "Bezpieczna kopia zapasowa",
"Allow Peer-to-Peer for 1:1 calls (if you enable this, the other party might be able to see your IP address)": "Pozwól na wykorzystanie peer-to-peer w rozmowach 1:1 (jeżeli włączono, druga strona może zobaczyć Twój adres IP)",
"Jump to the bottom of the timeline when you send a message": "Przejdź na dół osi czasu po wysłaniu wiadomości",
"Use Ctrl + F to search": "Używaj Ctrl + F do wyszukiwania",
"Show line numbers in code blocks": "Pokazuj numery wierszy w blokach kodu",
"Expand code blocks by default": "Domyślnie rozwijaj bloki kodu",
"Show stickers button": "Pokaż przycisk naklejek",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Zarządcy integracji otrzymują dane konfiguracji, mogą modyfikować widżety, wysyłać zaproszenia do pokoi i ustawiać poziom uprawnień w Twoim imieniu.",
"Converts the DM to a room": "Zmienia wiadomości bezpośrednie w pokój",
"Converts the room to a DM": "Zmienia pokój w wiadomość bezpośrednią",
"Sends the given message as a spoiler": "Wysyła podaną wiadomość jako spoiler",
"User Busy": "Użytkownik zajęty",
"The user you called is busy.": "Użytkownik do którego zadzwoniłeś(-aś) jest zajęty."
}

View file

@ -163,7 +163,7 @@
"%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.": "%(senderName)s enviou um convite para %(targetDisplayName)s entrar na sala.",
"%(senderName)s set a profile picture.": "%(senderName)s definiu uma foto de perfil.",
"%(senderName)s set their display name to %(displayName)s.": "%(senderName)s definiu o nome e sobrenome para %(displayName)s.",
"This email address is already in use": "Este endereço de e-mail já está em uso",
"This email address is already in use": "Este endereço de email já está em uso",
"This email address was not found": "Este endereço de e-mail não foi encontrado",
"The remote side failed to pick up": "A pessoa não atendeu a chamada",
"This room is not recognised.": "Esta sala não é reconhecida.",
@ -284,7 +284,7 @@
"ex. @bob:example.com": "p.ex: @joao:exemplo.com",
"Add User": "Adicionar usuária(o)",
"Custom Server Options": "Opções para Servidor Personalizado",
"Dismiss": "Descartar",
"Dismiss": "Dispensar",
"Please check your email to continue registration.": "Por favor, confirme o seu e-mail para continuar a inscrição.",
"Token incorrect": "Token incorreto",
"Please enter the code it contains:": "Por favor, entre com o código que está na mensagem:",
@ -1176,7 +1176,7 @@
"Sign In or Create Account": "Faça login ou crie uma conta",
"Use your account or create a new one to continue.": "Use sua conta ou crie uma nova para continuar.",
"Create Account": "Criar Conta",
"Sign In": "Entrar",
"Sign In": "Fazer signin",
"Custom (%(level)s)": "Personalizado (%(level)s)",
"Messages": "Mensagens",
"Actions": "Ações",

View file

@ -3361,5 +3361,14 @@
"sends space invaders": "dërgon pushtues hapësire",
"Sends the given message with a space themed effect": "E dërgon mesazhin e dhënë me një efekt teme hapësinore",
"See when people join, leave, or are invited to your active room": "Shihni kur persona vijnë, ikin ose janë ftuar në dhomën tuaj aktive",
"See when people join, leave, or are invited to this room": "Shihni kur persona vijnë, ikin ose janë ftuar në këtë dhomë"
"See when people join, leave, or are invited to this room": "Shihni kur persona vijnë, ikin ose janë ftuar në këtë dhomë",
"Space Autocomplete": "Vetëplotësim Hapësire",
"Currently joining %(count)s rooms|one": "Aktualisht duke hyrë në %(count)s dhomë",
"Currently joining %(count)s rooms|other": "Aktualisht duke hyrë në %(count)s dhoma",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Provoni fjalë të ndryshme, ose kontrolloni për gabime shkrimi. Disa përfundime mund të mos jenë të dukshme, ngaqë janë private dhe ju duhet një ftesë për të marrë pjesë në to.",
"No results for \"%(query)s\"": "Ska përfundime për \"%(query)s\"",
"The user you called is busy.": "Përdoruesi që thirrët është i zënë.",
"User Busy": "Përdoruesi Është i Zënë",
"Kick, ban, or invite people to your active room, and make you leave": "Përzini, dëboni, ose ftoni persona te dhoma juaj aktive, dhe bëni largimin tuaj",
"Kick, ban, or invite people to this room, and make you leave": "Përzini, dëboni, ose ftoni persona në këtë dhomë, dhe bëni largimin tuaj"
}

View file

@ -3305,5 +3305,11 @@
"See when people join, leave, or are invited to your active room": "Se när folk går med, lämnar eller bjuds in till ditt aktiva rum",
"Kick, ban, or invite people to your active room, and make you leave": "Kicka, banna eller bjuda in folk till ditt aktiva rum, och tvinga dig att lämna",
"See when people join, leave, or are invited to this room": "Se när folk går med, lämnar eller bjuds in till det här rummet",
"Kick, ban, or invite people to this room, and make you leave": "Kicka, banna eller bjuda in folk till det här rummet, och tvinga dig att lämna"
"Kick, ban, or invite people to this room, and make you leave": "Kicka, banna eller bjuda in folk till det här rummet, och tvinga dig att lämna",
"Currently joining %(count)s rooms|one": "Går just nu med i %(count)s rum",
"Currently joining %(count)s rooms|other": "Går just nu med i %(count)s rum",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "Testa andra ord eller kolla efter felskrivningar. Vissa resultat kanske inte visas för att de är privata och du behöver en inbjudan för att gå med i dem.",
"No results for \"%(query)s\"": "Inga resultat för \"%(query)s\"",
"The user you called is busy.": "Användaren du ringde är upptagen.",
"User Busy": "Användare upptagen"
}

View file

@ -267,8 +267,8 @@
"%(weekDayName)s, %(monthName)s %(day)s %(fullYear)s %(time)s": "%(weekDayName)s, %(day)s %(monthName)s %(fullYear)s %(time)s",
"Who would you like to add to this community?": "Кого ви хочете додати до цієї спільноти?",
"Where this page includes identifiable information, such as a room, user or group ID, that data is removed before being sent to the server.": "Там, де ця сторінка містить ототожненну інформацію, як-от назва кімнати, користувача чи групи, ці дані будуть вилучені перед надсиланням на сервер.",
"Call in Progress": "Іде виклик",
"A call is currently being placed!": "Зараз іде виклик!",
"Call in Progress": "Триває виклик",
"A call is currently being placed!": "Зараз триває виклик!",
"A call is already in progress!": "Вже здійснюється дзвінок!",
"Permission Required": "Потрібен дозвіл",
"You do not have permission to start a conference call in this room": "У вас немає дозволу, щоб розпочати дзвінок-конференцію в цій кімнаті",
@ -306,8 +306,8 @@
"Missing user_id in request": "У запиті пропущено user_id",
"Usage": "Використання",
"Searches DuckDuckGo for results": "Здійснює пошук через DuckDuckGo",
"/ddg is not a command": "/ddg — це не команда",
"To use it, just wait for autocomplete results to load and tab through them.": "Щоб цим скористатися, просто почекайте на підказки доповнення й перемикайтеся між ними клавішею TAB.",
"/ddg is not a command": "/ddg не є командою",
"To use it, just wait for autocomplete results to load and tab through them.": "Щоб цим скористатися, просто почекайте на підказки автодоповнення й перемикайтеся між ними клавішею TAB.",
"Changes your display nickname": "Змінює ваш нік",
"Invites user with given id to current room": "Запрошує користувача з вказаним ідентифікатором до кімнати",
"Leave room": "Залишити кімнату",
@ -457,7 +457,7 @@
"Actions": "Дії",
"Other": "Інше",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Додає ¯\\_(ツ)_/¯ на початку текстового повідомлення",
"Sends a message as plain text, without interpreting it as markdown": "Надсилає повідомлення як чистий текст, не використовуючи markdown",
"Sends a message as plain text, without interpreting it as markdown": "Надсилає повідомлення у вигляді звичайного тексту, не інтерпретуючи його як розмітку",
"Upgrades a room to a new version": "Покращує кімнату до нової версії",
"You do not have the required permissions to use this command.": "Вам бракує дозволу на використання цієї команди.",
"<b>Warning</b>: Upgrading a room will <i>not automatically migrate room members to the new version of the room.</i> We'll post a link to the new room in the old version of the room - room members will have to click this link to join the new room.": "<b>Увага!</b>: Поліпшення кімнати <i>не перенесе автоматично усіх учасників до нової версії кімнати.</i> Ми опублікуємо посилання на нову кімнату у старій версії кімнати, а учасники мають власноруч клацнути це посилання, щоб приєднатися до нової кімнати.",
@ -624,7 +624,7 @@
"Riot is now Element!": "Riot тепер - Element!",
"Learn More": "Дізнатися більше",
"Command error": "Помилка команди",
"Sends a message as html, without interpreting it as markdown": "Надсилає повідомлення як HTML, не інтерпритуючи Markdown",
"Sends a message as html, without interpreting it as markdown": "Надсилає повідомлення у вигляді HTML, не інтерпретуючи його як розмітку",
"Failed to set topic": "Не вдалося встановити тему",
"Once enabled, encryption cannot be disabled.": "Після увімкнення шифрування не можна буде вимкнути.",
"Please enter verification code sent via text.": "Будь ласка, введіть звірювальний код, відправлений у текстовому повідомленні.",
@ -1602,5 +1602,33 @@
"Share Link to User": "Поділитися посиланням на користувача",
"Messages here are end-to-end encrypted. Verify %(displayName)s in their profile - tap on their avatar.": "Повідомлення тут захищено наскрізним шифруванням. Підтвердьте %(displayName)s у їхньому профілі — натиснувши на їх аватар.",
"Open": "Відкрити",
"<a>In reply to</a> <pill>": "<a>У відповідь на</a> <pill>"
"<a>In reply to</a> <pill>": "<a>У відповідь на</a> <pill>",
"The user you called is busy.": "Користувач, якого ви викликаєте, зайнятий.",
"User Busy": "Користувач зайнятий",
"Prepends ┬──┬ ( ゜-゜ノ) to a plain-text message": "Додає ┬──┬ ( ゜-゜ノ) на початку текстового повідомлення",
"Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message": "Додає (╯°□°)╯︵ ┻━┻ на початку текстового повідомлення",
"We couldn't log you in": "Нам не вдалося виконати вхід",
"You're already in a call with this person.": "Ви вже спілкуєтесь із цією особою.",
"Already in call": "Вже у виклику",
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "Ви не можете надсилати жодних повідомлень, поки не переглянете та не погодитесь з <consentLink>нашими умовами та положеннями</consentLink>.",
"Send message": "Надіслати повідомлення",
"Sending your message...": "Надсилання повідомлення...",
"You can use <code>/help</code> to list available commands. Did you mean to send this as a message?": "Ви можете скористатися <code>/help</code> для перегляду доступних команд. Ви мали намір надіслати це як повідомлення?",
"Send messages": "Надіслати повідомлення",
"sends confetti": "надсилає конфеті",
"sends fireworks": "надсилає феєрверк",
"sends space invaders": "надсилає тему про космічних загарбників",
"Sends the given message with a space themed effect": "Надсилає це повідомлення з космічними ефектами",
"unknown person": "невідома особа",
"Sends the given message with snowfall": "Надсилає це повідомлення зі снігопадом",
"Sends the given message with fireworks": "Надсилає це повідомлення з феєрверком",
"Sends the given message with confetti": "Надсилає це повідомлення з конфеті",
"Use Ctrl + Enter to send a message": "Натисніть Ctrl + Enter, щоб надіслати повідомлення",
"Use Command + Enter to send a message": "Натисніть Command + Enter, щоб надіслати повідомлення",
"Use Ctrl + F to search": "Натисніть Ctrl + F, щоб шукати",
"Use Command + F to search": "Натисніть Command + F, щоб шукати",
"Send text messages as you in this room": "Надіслати текстові повідомлення у цю кімнату від свого імені",
"Send messages as you in your active room": "Надіслати повідомлення у свою активну кімнату від свого імені",
"Send messages as you in this room": "Надіслати повідомлення у цю кімнату від свого імені",
"Sends the given message as a spoiler": "Надсилає вказане повідомлення згорненим"
}

View file

@ -3274,5 +3274,11 @@
"Send stickers to your active room as you": "发送贴纸到你所活跃的聊天室",
"See when people join, leave, or are invited to your active room": "查看人们何时加入、离开或被邀请到你所活跃的聊天室",
"See when people join, leave, or are invited to this room": "查看人们何时加入、离开或被邀请到这个房间",
"Kick, ban, or invite people to this room, and make you leave": "移除、封禁或邀请用户到此聊天室,并让你离开"
"Kick, ban, or invite people to this room, and make you leave": "移除、封禁或邀请用户到此聊天室,并让你离开",
"Currently joining %(count)s rooms|one": "目前正在加入 %(count)s 个聊天室",
"Currently joining %(count)s rooms|other": "目前正在加入 %(count)s 个聊天室",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "尝试不同的单词或检查拼写错误。某些结果可能不可见,因为它们属于私有的,你需要一个邀请才能加入。",
"No results for \"%(query)s\"": "「%(query)s」没有结果",
"The user you called is busy.": "你所拨打的用户正在忙碌中。",
"User Busy": "用户正在忙"
}

View file

@ -3378,5 +3378,11 @@
"See when people join, leave, or are invited to your active room": "檢視人們何時加入、離開或被邀請至您活躍的聊天室",
"Kick, ban, or invite people to your active room, and make you leave": "踢除、封鎖或邀請人們到您作用中的聊天室,然後讓您離開",
"See when people join, leave, or are invited to this room": "檢視人們何時加入、離開或被邀請至此聊天室",
"Kick, ban, or invite people to this room, and make you leave": "踢除、封鎖或邀請人們到此聊天室,然後讓您離開"
"Kick, ban, or invite people to this room, and make you leave": "踢除、封鎖或邀請人們到此聊天室,然後讓您離開",
"Currently joining %(count)s rooms|one": "目前正在加入 %(count)s 個聊天室",
"Currently joining %(count)s rooms|other": "目前正在加入 %(count)s 個聊天室",
"Try different words or check for typos. Some results may not be visible as they're private and you need an invite to join them.": "嘗試不同的詞或是檢查拼字。某些結果可能不可見,因為其為私人的,您必須要有邀請才能加入。",
"No results for \"%(query)s\"": "「%(query)s」沒有結果",
"The user you called is busy.": "您想要通話的使用者目前忙碌中。",
"User Busy": "使用者忙碌"
}

View file

@ -300,7 +300,7 @@ export default class EventIndex extends EventEmitter {
}
private eventToJson(ev: MatrixEvent) {
const jsonEvent = ev.toJSON();
const jsonEvent: any = ev.toJSON();
const e = ev.isEncrypted() ? jsonEvent.decrypted : jsonEvent;
if (ev.isEncrypted()) {

View file

@ -37,17 +37,17 @@ export enum PerformanceEntryNames {
SWITCH_ROOM = "mx_SwithRoom",
JUMP_TO_ROOM = "mx_JumpToRoom",
JOIN_ROOM = "mx_JoinRoom",
CREATE_DM = "mx_CreateDM",
JOIN_ROOM = "mx_JoinRoom", // ✅
CREATE_DM = "mx_CreateDM", // ✅
PEEK_ROOM = "mx_PeekRoom",
/**
* User
*/
VERIFY_E2EE_USER = "mx_VerifyE2EEUser",
LOGIN = "mx_Login",
REGISTER = "mx_Register",
VERIFY_E2EE_USER = "mx_VerifyE2EEUser", // ✅
LOGIN = "mx_Login", // ✅
REGISTER = "mx_Register", // ✅
/**
* VoIP

View file

@ -16,8 +16,8 @@ limitations under the License.
import EventEmitter from 'events';
import { IWidget } from 'matrix-widget-api';
import MatrixEvent from "matrix-js-sdk/src/models/event";
import {WidgetType} from "../widgets/WidgetType";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { WidgetType } from "../widgets/WidgetType";
/**
* Acts as a place to get & set widget state, storing local echo state and

View file

@ -51,7 +51,7 @@ import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
import {getCustomTheme} from "../../theme";
import CountlyAnalytics from "../../CountlyAnalytics";
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { MatrixEvent, IEvent } from "matrix-js-sdk/src/models/event";
import { ELEMENT_CLIENT_ID } from "../../identifiers";
import { getUserLanguage } from "../../languageHandler";
@ -415,7 +415,7 @@ export class StopGapWidget extends EventEmitter {
private feedEvent(ev: MatrixEvent) {
if (!this.messaging) return;
const raw = ev.event;
const raw = ev.event as IEvent;
this.messaging.feedEvent(raw).catch(e => {
console.error("Error sending event to widget: ", e);
});

View file

@ -145,7 +145,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
return {roomId, eventId: r.event_id};
}
public async readRoomEvents(eventType: string, msgtype: string | undefined, limit: number): Promise<MatrixEvent[]> {
public async readRoomEvents(eventType: string, msgtype: string | undefined, limit: number): Promise<object[]> {
limit = limit > 0 ? Math.min(limit, 25) : 25; // arbitrary choice
const client = MatrixClientPeg.get();
@ -167,9 +167,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
return results.map(e => e.event);
}
public async readStateEvents(
eventType: string, stateKey: string | undefined, limit: number,
): Promise<MatrixEvent[]> {
public async readStateEvents(eventType: string, stateKey: string | undefined, limit: number): Promise<object[]> {
limit = limit > 0 ? Math.min(limit, 100) : 100; // arbitrary choice
const client = MatrixClientPeg.get();
@ -178,7 +176,7 @@ export class StopGapWidgetDriver extends WidgetDriver {
if (!client || !roomId || !room) throw new Error("Not in a room or not attached to a client");
const results: MatrixEvent[] = [];
const state = room.currentState.events.get(eventType);
const state: Map<string, MatrixEvent> = room.currentState.events.get(eventType);
if (state) {
if (stateKey === "" || !!stateKey) {
const forKey = state.get(stateKey);

View file

@ -14,11 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {MatrixClientPeg} from '../MatrixClientPeg';
import {uniq} from "lodash";
import {Room} from "matrix-js-sdk/src/models/room";
import {Event} from "matrix-js-sdk/src/models/event";
import {MatrixClient} from "matrix-js-sdk/src/client";
import { uniq } from "lodash";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { MatrixClientPeg } from '../MatrixClientPeg';
/**
* Class that takes a Matrix Client and flips the m.direct map
@ -30,15 +30,13 @@ import {MatrixClient} from "matrix-js-sdk/src/client";
export default class DMRoomMap {
private static sharedInstance: DMRoomMap;
private matrixClient: MatrixClient;
// TODO: convert these to maps
private roomToUser: {[key: string]: string} = null;
private userToRooms: {[key: string]: string[]} = null;
private hasSentOutPatchDirectAccountDataPatch: boolean;
private mDirectEvent: Event;
private mDirectEvent: object;
constructor(matrixClient) {
this.matrixClient = matrixClient;
constructor(private readonly matrixClient: MatrixClient) {
// see onAccountData
this.hasSentOutPatchDirectAccountDataPatch = false;

View file

@ -392,7 +392,7 @@ export default class WidgetUtils {
}
const widgets = client.getAccountData('m.widgets');
if (!widgets) return;
const userWidgets: IWidgetEvent[] = widgets.getContent() || {};
const userWidgets: Record<string, IWidgetEvent> = widgets.getContent() || {};
Object.entries(userWidgets).forEach(([key, widget]) => {
if (widget.content && widget.content.type === "m.integration_manager") {
delete userWidgets[key];

View file

@ -30,9 +30,7 @@ function createFailedDecryptionEvent() {
const event = new MatrixEvent({
event_id: "event-id-" + Math.random().toString(16).slice(2),
});
event._setClearData(
event._badEncryptedMessage(":("),
);
event.setClearData(event.badEncryptedMessage(":("));
return event;
}
@ -67,7 +65,7 @@ describe('DecryptionFailureTracker', function() {
tracker.eventDecrypted(decryptedEvent, err);
// Indicate successful decryption: clear data can be anything where the msgtype is not m.bad.encrypted
decryptedEvent._setClearData({});
decryptedEvent.setClearData({});
tracker.eventDecrypted(decryptedEvent, null);
// Pretend "now" is Infinity

View file

@ -20,9 +20,11 @@ const acceptInvite = require('../usecases/accept-invite');
const {receiveMessage} = require('../usecases/timeline');
const {createDm} = require('../usecases/create-room');
const {checkRoomSettings} = require('../usecases/room-settings');
const {startSasVerifcation, acceptSasVerification} = require('../usecases/verify');
const {startSasVerification, acceptSasVerification} = require('../usecases/verify');
const { setupSecureBackup } = require('../usecases/security');
const assert = require('assert');
const { measureStart, measureStop } = require('../util');
module.exports = async function e2eEncryptionScenarios(alice, bob) {
console.log(" creating an e2e encrypted DM and join through invite:");
@ -31,12 +33,14 @@ module.exports = async function e2eEncryptionScenarios(alice, bob) {
await acceptInvite(alice, 'bob');
// do sas verifcation
bob.log.step(`starts SAS verification with ${alice.username}`);
const bobSasPromise = startSasVerifcation(bob, alice.username);
await measureStart(bob, "mx_VerifyE2EEUser");
const bobSasPromise = startSasVerification(bob, alice.username);
const aliceSasPromise = acceptSasVerification(alice, bob.username);
// wait in parallel, so they don't deadlock on each other
// the logs get a bit messy here, but that's fine enough for debugging (hopefully)
const [bobSas, aliceSas] = await Promise.all([bobSasPromise, aliceSasPromise]);
assert.deepEqual(bobSas, aliceSas);
await measureStop(bob, "mx_VerifyE2EEUser");
bob.log.done(`done (match for ${bobSas.join(", ")})`);
const aliceMessage = "Guess what I just heard?!";
await sendMessage(alice, aliceMessage);

View file

@ -15,6 +15,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
const { measureStart, measureStop } = require('../util');
async function openRoomDirectory(session) {
const roomDirectoryButton = await session.query('.mx_LeftPanel_exploreButton');
await roomDirectoryButton.click();
@ -52,6 +54,8 @@ async function createRoom(session, roomName, encrypted=false) {
async function createDm(session, invitees) {
session.log.step(`creates DM with ${JSON.stringify(invitees)}`);
await measureStart(session, "mx_CreateDM");
const dmsSublist = await findSublist(session, "people");
const startChatButton = await dmsSublist.$(".mx_RoomSublist_auxButton");
await startChatButton.click();
@ -76,6 +80,8 @@ async function createDm(session, invitees) {
await session.query('.mx_MessageComposer');
session.log.done();
await measureStop(session, "mx_CreateDM");
}
module.exports = {openRoomDirectory, findSublist, createRoom, createDm};

View file

@ -16,9 +16,12 @@ limitations under the License.
*/
const {openRoomDirectory} = require('./create-room');
const { measureStart, measureStop } = require('../util');
module.exports = async function join(session, roomName) {
session.log.step(`joins room "${roomName}"`);
await measureStart(session, "mx_JoinRoom");
await openRoomDirectory(session);
const roomInput = await session.query('.mx_DirectorySearchBox input');
await session.replaceInputText(roomInput, roomName);
@ -26,5 +29,6 @@ module.exports = async function join(session, roomName) {
const joinFirstLink = await session.query('.mx_RoomDirectory_table .mx_RoomDirectory_join .mx_AccessibleButton');
await joinFirstLink.click();
await session.query('.mx_MessageComposer');
await measureStop(session, "mx_JoinRoom");
session.log.done();
};

View file

@ -74,7 +74,7 @@ async function doSasVerification(session) {
return sasCodes;
}
module.exports.startSasVerifcation = async function(session, name) {
module.exports.startSasVerification = async function(session, name) {
session.log.startGroup("starts verification");
await startVerification(session, name);

View file

@ -26,3 +26,15 @@ module.exports.range = function(start, amount, step = 1) {
module.exports.delay = function(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
};
module.exports.measureStart = function(session, name) {
return session.page.evaluate(_name => {
window.mxPerformanceMonitor.start(_name);
}, name);
};
module.exports.measureStop = function(session, name) {
return session.page.evaluate(_name => {
window.mxPerformanceMonitor.stop(_name);
}, name);
};

View file

@ -79,7 +79,7 @@ async function runTests() {
await new Promise((resolve) => setTimeout(resolve, 5 * 60 * 1000));
}
const performanceEntries = {};
let performanceEntries;
await Promise.all(sessions.map(async (session) => {
// Collecting all performance monitoring data before closing the session
@ -88,6 +88,10 @@ async function runTests() {
window.mxPerformanceMonitor.addPerformanceDataCallback({
entryNames: [
window.mxPerformanceEntryNames.REGISTER,
window.mxPerformanceEntryNames.LOGIN,
window.mxPerformanceEntryNames.JOIN_ROOM,
window.mxPerformanceEntryNames.CREATE_DM,
window.mxPerformanceEntryNames.VERIFY_E2EE_USER,
],
callback: (events) => {
measurements = JSON.stringify(events);
@ -95,7 +99,11 @@ async function runTests() {
}, true);
return measurements;
});
performanceEntries[session.username] = JSON.parse(measurements);
/**
* TODO: temporary only use one user session data
*/
performanceEntries = JSON.parse(measurements);
return session.close();
}));
fs.writeFileSync(`performance-entries.json`, JSON.stringify(performanceEntries));

View file

@ -5733,10 +5733,10 @@ matrix-mock-request@^1.2.3:
bluebird "^3.5.0"
expect "^1.20.2"
matrix-react-test-utils@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.2.tgz#c87144d3b910c7edc544a6699d13c7c2bf02f853"
integrity sha512-49+7gfV6smvBIVbeloql+37IeWMTD+fiywalwCqk8Dnz53zAFjKSltB3rmWHso1uecLtQEcPtCijfhzcLXAxTQ==
matrix-react-test-utils@^0.2.3:
version "0.2.3"
resolved "https://registry.yarnpkg.com/matrix-react-test-utils/-/matrix-react-test-utils-0.2.3.tgz#27653f9d6bbfddd1856e51860fad1503b039d617"
integrity sha512-NKZDlMEQzDZDQhBYyKBUtqidRvpkww3n9/GmGICkxtU2D6NetyBIfvm1Lf9o7167KSkPHJUVvDS9dzaS55jUnA==
"matrix-web-i18n@github:matrix-org/matrix-web-i18n":
version "1.1.2"
@ -6583,9 +6583,9 @@ postcss-value-parser@^4.1.0:
integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==
postcss@^7.0.14, postcss@^7.0.2, postcss@^7.0.21, postcss@^7.0.26, postcss@^7.0.32, postcss@^7.0.35, postcss@^7.0.6:
version "7.0.35"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24"
integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg==
version "7.0.36"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.36.tgz#056f8cffa939662a8f5905950c07d5285644dfcb"
integrity sha512-BebJSIUMwJHRH0HAQoxN4u1CN86glsrwsW0q7T+/m44eXOUAxSNdHRkNZPYz5vVUbg17hFgOQDE7fZk7li3pZw==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"