Merge branch 'develop' into luke/groups-are-communities
This commit is contained in:
commit
0c34e943fb
38 changed files with 642 additions and 58 deletions
157
CHANGELOG.md
157
CHANGELOG.md
|
@ -1,3 +1,160 @@
|
||||||
|
Changes in [0.10.7](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.7) (2017-10-16)
|
||||||
|
=====================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.7-rc.3...v0.10.7)
|
||||||
|
|
||||||
|
* Update to latest js-sdk
|
||||||
|
|
||||||
|
Changes in [0.10.7-rc.3](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.7-rc.3) (2017-10-13)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.7-rc.2...v0.10.7-rc.3)
|
||||||
|
|
||||||
|
* Fix the enableLabs flag, again
|
||||||
|
[\#1474](https://github.com/matrix-org/matrix-react-sdk/pull/1474)
|
||||||
|
|
||||||
|
Changes in [0.10.7-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.7-rc.2) (2017-10-13)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.7-rc.1...v0.10.7-rc.2)
|
||||||
|
|
||||||
|
* Honour the (now legacy) enableLabs flag
|
||||||
|
[\#1473](https://github.com/matrix-org/matrix-react-sdk/pull/1473)
|
||||||
|
* Don't show labs features by default
|
||||||
|
[\#1472](https://github.com/matrix-org/matrix-react-sdk/pull/1472)
|
||||||
|
* Make features disabled by default
|
||||||
|
[\#1470](https://github.com/matrix-org/matrix-react-sdk/pull/1470)
|
||||||
|
|
||||||
|
Changes in [0.10.7-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.7-rc.1) (2017-10-13)
|
||||||
|
===============================================================================================================
|
||||||
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.6...v0.10.7-rc.1)
|
||||||
|
|
||||||
|
* Add warm fuzzy dialog for inviting users to a group
|
||||||
|
[\#1459](https://github.com/matrix-org/matrix-react-sdk/pull/1459)
|
||||||
|
* enable/disable features in config.json
|
||||||
|
[\#1468](https://github.com/matrix-org/matrix-react-sdk/pull/1468)
|
||||||
|
* Update from Weblate.
|
||||||
|
[\#1469](https://github.com/matrix-org/matrix-react-sdk/pull/1469)
|
||||||
|
* Don't send RR or RM when peeking at a room
|
||||||
|
[\#1463](https://github.com/matrix-org/matrix-react-sdk/pull/1463)
|
||||||
|
* Fix bug that inserted emoji when typing
|
||||||
|
[\#1467](https://github.com/matrix-org/matrix-react-sdk/pull/1467)
|
||||||
|
* Ignore VS16 char in RTE
|
||||||
|
[\#1458](https://github.com/matrix-org/matrix-react-sdk/pull/1458)
|
||||||
|
* Show failures when sending messages
|
||||||
|
[\#1460](https://github.com/matrix-org/matrix-react-sdk/pull/1460)
|
||||||
|
* Run eslint --fix
|
||||||
|
[\#1461](https://github.com/matrix-org/matrix-react-sdk/pull/1461)
|
||||||
|
* Show who banned the user on hover
|
||||||
|
[\#1441](https://github.com/matrix-org/matrix-react-sdk/pull/1441)
|
||||||
|
* Enhancements to room power level settings
|
||||||
|
[\#1440](https://github.com/matrix-org/matrix-react-sdk/pull/1440)
|
||||||
|
* Added TextInputWithCheckbox dialog
|
||||||
|
[\#868](https://github.com/matrix-org/matrix-react-sdk/pull/868)
|
||||||
|
* Make it clearer which HS you're logging into
|
||||||
|
[\#1456](https://github.com/matrix-org/matrix-react-sdk/pull/1456)
|
||||||
|
* Remove redundant stale onKeyDown
|
||||||
|
[\#1451](https://github.com/matrix-org/matrix-react-sdk/pull/1451)
|
||||||
|
* Only allow event state event handlers on state events
|
||||||
|
[\#1453](https://github.com/matrix-org/matrix-react-sdk/pull/1453)
|
||||||
|
* Modify the group store to include group rooms
|
||||||
|
[\#1452](https://github.com/matrix-org/matrix-react-sdk/pull/1452)
|
||||||
|
* Factor-out GroupStore and create GroupStoreCache
|
||||||
|
[\#1449](https://github.com/matrix-org/matrix-react-sdk/pull/1449)
|
||||||
|
* Put related groups UI behind groups labs flag
|
||||||
|
[\#1448](https://github.com/matrix-org/matrix-react-sdk/pull/1448)
|
||||||
|
* Restrict Flair in the timeline to related groups of the room
|
||||||
|
[\#1447](https://github.com/matrix-org/matrix-react-sdk/pull/1447)
|
||||||
|
* Implement UI for editing related groups of a room
|
||||||
|
[\#1446](https://github.com/matrix-org/matrix-react-sdk/pull/1446)
|
||||||
|
* Fix a couple of bugs with EditableItemList
|
||||||
|
[\#1445](https://github.com/matrix-org/matrix-react-sdk/pull/1445)
|
||||||
|
* Factor out EditableItemList from AliasSettings
|
||||||
|
[\#1444](https://github.com/matrix-org/matrix-react-sdk/pull/1444)
|
||||||
|
* Add dummy translation function to mark translatable strings
|
||||||
|
[\#1421](https://github.com/matrix-org/matrix-react-sdk/pull/1421)
|
||||||
|
* Implement button to remove a room from a group
|
||||||
|
[\#1438](https://github.com/matrix-org/matrix-react-sdk/pull/1438)
|
||||||
|
* Fix showing 3pid invites in member list
|
||||||
|
[\#1443](https://github.com/matrix-org/matrix-react-sdk/pull/1443)
|
||||||
|
* Add button to get to MyGroups (view_my_groups or path #/groups)
|
||||||
|
[\#1435](https://github.com/matrix-org/matrix-react-sdk/pull/1435)
|
||||||
|
* Add eslint rule to disallow spaces inside of curly braces
|
||||||
|
[\#1436](https://github.com/matrix-org/matrix-react-sdk/pull/1436)
|
||||||
|
* Fix ability to invite existing mx users
|
||||||
|
[\#1437](https://github.com/matrix-org/matrix-react-sdk/pull/1437)
|
||||||
|
* Construct address picker message using provided `validAddressTypes`
|
||||||
|
[\#1434](https://github.com/matrix-org/matrix-react-sdk/pull/1434)
|
||||||
|
* Fix GroupView summary rooms displaying without avatars
|
||||||
|
[\#1433](https://github.com/matrix-org/matrix-react-sdk/pull/1433)
|
||||||
|
* Implement adding rooms to a group (or group summary) by room ID
|
||||||
|
[\#1432](https://github.com/matrix-org/matrix-react-sdk/pull/1432)
|
||||||
|
* Give flair avatars a tooltip = the group ID
|
||||||
|
[\#1431](https://github.com/matrix-org/matrix-react-sdk/pull/1431)
|
||||||
|
* Fix ability to feature self in a group summary
|
||||||
|
[\#1430](https://github.com/matrix-org/matrix-react-sdk/pull/1430)
|
||||||
|
* Implement "Add room to group" feature
|
||||||
|
[\#1429](https://github.com/matrix-org/matrix-react-sdk/pull/1429)
|
||||||
|
* Fix group membership publicity
|
||||||
|
[\#1428](https://github.com/matrix-org/matrix-react-sdk/pull/1428)
|
||||||
|
* Add support for Jitsi screensharing in electron app
|
||||||
|
[\#1355](https://github.com/matrix-org/matrix-react-sdk/pull/1355)
|
||||||
|
* Delint and DRY TextForEvent
|
||||||
|
[\#1424](https://github.com/matrix-org/matrix-react-sdk/pull/1424)
|
||||||
|
* Bust the flair caches after 30mins
|
||||||
|
[\#1427](https://github.com/matrix-org/matrix-react-sdk/pull/1427)
|
||||||
|
* Show displayname / avatar in group member info
|
||||||
|
[\#1426](https://github.com/matrix-org/matrix-react-sdk/pull/1426)
|
||||||
|
* Create GroupSummaryStore for storing group summary stuff
|
||||||
|
[\#1418](https://github.com/matrix-org/matrix-react-sdk/pull/1418)
|
||||||
|
* Add status & toggle for publicity
|
||||||
|
[\#1419](https://github.com/matrix-org/matrix-react-sdk/pull/1419)
|
||||||
|
* MemberList: show 100 more on overflow tile click
|
||||||
|
[\#1417](https://github.com/matrix-org/matrix-react-sdk/pull/1417)
|
||||||
|
* Fix NPE in MemberList
|
||||||
|
[\#1425](https://github.com/matrix-org/matrix-react-sdk/pull/1425)
|
||||||
|
* Fix incorrect variable in string
|
||||||
|
[\#1422](https://github.com/matrix-org/matrix-react-sdk/pull/1422)
|
||||||
|
* apply i18n _t to string which has already been translated
|
||||||
|
[\#1420](https://github.com/matrix-org/matrix-react-sdk/pull/1420)
|
||||||
|
* Make the invite section a truncatedlist too
|
||||||
|
[\#1416](https://github.com/matrix-org/matrix-react-sdk/pull/1416)
|
||||||
|
* Implement removal function of features users/rooms
|
||||||
|
[\#1415](https://github.com/matrix-org/matrix-react-sdk/pull/1415)
|
||||||
|
* Allow TruncatedList to get children via a callback
|
||||||
|
[\#1412](https://github.com/matrix-org/matrix-react-sdk/pull/1412)
|
||||||
|
* Experimental: Lazy load user autocomplete entries
|
||||||
|
[\#1413](https://github.com/matrix-org/matrix-react-sdk/pull/1413)
|
||||||
|
* Show displayname & avatar url in group member list
|
||||||
|
[\#1414](https://github.com/matrix-org/matrix-react-sdk/pull/1414)
|
||||||
|
* De-lint TruncatedList
|
||||||
|
[\#1411](https://github.com/matrix-org/matrix-react-sdk/pull/1411)
|
||||||
|
* Remove unneeded strings
|
||||||
|
[\#1409](https://github.com/matrix-org/matrix-react-sdk/pull/1409)
|
||||||
|
* Clean on prerelease
|
||||||
|
[\#1410](https://github.com/matrix-org/matrix-react-sdk/pull/1410)
|
||||||
|
* Redesign membership section in GroupView
|
||||||
|
[\#1408](https://github.com/matrix-org/matrix-react-sdk/pull/1408)
|
||||||
|
* Implement adding rooms to the group summary
|
||||||
|
[\#1406](https://github.com/matrix-org/matrix-react-sdk/pull/1406)
|
||||||
|
* Honour the is_privileged flag in GroupView
|
||||||
|
[\#1407](https://github.com/matrix-org/matrix-react-sdk/pull/1407)
|
||||||
|
* Update when a group arrives
|
||||||
|
[\#1405](https://github.com/matrix-org/matrix-react-sdk/pull/1405)
|
||||||
|
* Implement `view_group` dispatch when clicking flair
|
||||||
|
[\#1404](https://github.com/matrix-org/matrix-react-sdk/pull/1404)
|
||||||
|
* GroupView: Add a User
|
||||||
|
[\#1402](https://github.com/matrix-org/matrix-react-sdk/pull/1402)
|
||||||
|
* Track action button click event
|
||||||
|
[\#1403](https://github.com/matrix-org/matrix-react-sdk/pull/1403)
|
||||||
|
* Separate sender profile into elements with classes
|
||||||
|
[\#1401](https://github.com/matrix-org/matrix-react-sdk/pull/1401)
|
||||||
|
* Fix ugly integration button, use hover to show error
|
||||||
|
[\#1399](https://github.com/matrix-org/matrix-react-sdk/pull/1399)
|
||||||
|
* Fix promise error in flair
|
||||||
|
[\#1400](https://github.com/matrix-org/matrix-react-sdk/pull/1400)
|
||||||
|
* Flair!
|
||||||
|
[\#1351](https://github.com/matrix-org/matrix-react-sdk/pull/1351)
|
||||||
|
* Group Membership UI
|
||||||
|
[\#1328](https://github.com/matrix-org/matrix-react-sdk/pull/1328)
|
||||||
|
|
||||||
Changes in [0.10.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.6) (2017-09-21)
|
Changes in [0.10.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v0.10.6) (2017-09-21)
|
||||||
=====================================================================================================
|
=====================================================================================================
|
||||||
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.5...v0.10.6)
|
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v0.10.5...v0.10.6)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "matrix-react-sdk",
|
"name": "matrix-react-sdk",
|
||||||
"version": "0.10.6",
|
"version": "0.10.7",
|
||||||
"description": "SDK for matrix.org using React",
|
"description": "SDK for matrix.org using React",
|
||||||
"author": "matrix.org",
|
"author": "matrix.org",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -67,7 +67,7 @@
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"linkifyjs": "^2.1.3",
|
"linkifyjs": "^2.1.3",
|
||||||
"lodash": "^4.13.1",
|
"lodash": "^4.13.1",
|
||||||
"matrix-js-sdk": "0.8.4",
|
"matrix-js-sdk": "0.8.5",
|
||||||
"optimist": "^0.6.1",
|
"optimist": "^0.6.1",
|
||||||
"prop-types": "^15.5.8",
|
"prop-types": "^15.5.8",
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
|
|
|
@ -172,7 +172,7 @@ const sanitizeHtmlParams = {
|
||||||
// Lots of these won't come up by default because we don't allow them
|
// Lots of these won't come up by default because we don't allow them
|
||||||
selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'],
|
selfClosing: ['img', 'br', 'hr', 'area', 'base', 'basefont', 'input', 'link', 'meta'],
|
||||||
// URL schemes we permit
|
// URL schemes we permit
|
||||||
allowedSchemes: ['http', 'https', 'ftp', 'mailto'],
|
allowedSchemes: ['http', 'https', 'ftp', 'mailto', 'magnet'],
|
||||||
|
|
||||||
allowProtocolRelative: false,
|
allowProtocolRelative: false,
|
||||||
|
|
||||||
|
@ -385,10 +385,9 @@ class TextHighlighter extends BaseHighlighter {
|
||||||
* highlights: optional list of words to highlight, ordered by longest word first
|
* highlights: optional list of words to highlight, ordered by longest word first
|
||||||
*
|
*
|
||||||
* opts.highlightLink: optional href to add to highlighted words
|
* opts.highlightLink: optional href to add to highlighted words
|
||||||
|
* opts.disableBigEmoji: optional argument to disable the big emoji class.
|
||||||
*/
|
*/
|
||||||
export function bodyToHtml(content, highlights, opts) {
|
export function bodyToHtml(content, highlights, opts={}) {
|
||||||
opts = opts || {};
|
|
||||||
|
|
||||||
const isHtml = (content.format === "org.matrix.custom.html");
|
const isHtml = (content.format === "org.matrix.custom.html");
|
||||||
const body = isHtml ? content.formatted_body : escape(content.body);
|
const body = isHtml ? content.formatted_body : escape(content.body);
|
||||||
|
|
||||||
|
@ -418,7 +417,7 @@ export function bodyToHtml(content, highlights, opts) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let emojiBody = false;
|
let emojiBody = false;
|
||||||
if (bodyHasEmoji) {
|
if (!opts.disableBigEmoji && bodyHasEmoji) {
|
||||||
EMOJI_REGEX.lastIndex = 0;
|
EMOJI_REGEX.lastIndex = 0;
|
||||||
const contentBodyTrimmed = content.body !== undefined ? content.body.trim() : '';
|
const contentBodyTrimmed = content.body !== undefined ? content.body.trim() : '';
|
||||||
const match = EMOJI_REGEX.exec(contentBodyTrimmed);
|
const match = EMOJI_REGEX.exec(contentBodyTrimmed);
|
||||||
|
|
|
@ -80,10 +80,11 @@ const Notifier = {
|
||||||
if (ev.getContent().body) msg = ev.getContent().body;
|
if (ev.getContent().body) msg = ev.getContent().body;
|
||||||
}
|
}
|
||||||
|
|
||||||
const avatarUrl = ev.sender ? Avatar.avatarUrlForMember(
|
if (!this.isBodyEnabled()) {
|
||||||
ev.sender, 40, 40, 'crop',
|
msg = '';
|
||||||
) : null;
|
}
|
||||||
|
|
||||||
|
const avatarUrl = ev.sender ? Avatar.avatarUrlForMember(ev.sender, 40, 40, 'crop') : null;
|
||||||
const notif = plaf.displayNotification(title, msg, avatarUrl, room);
|
const notif = plaf.displayNotification(title, msg, avatarUrl, room);
|
||||||
|
|
||||||
// if displayNotification returns non-null, the platform supports
|
// if displayNotification returns non-null, the platform supports
|
||||||
|
@ -195,6 +196,19 @@ const Notifier = {
|
||||||
return enabled === 'true';
|
return enabled === 'true';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setBodyEnabled: function(enable) {
|
||||||
|
if (!global.localStorage) return;
|
||||||
|
global.localStorage.setItem('notifications_body_enabled', enable ? 'true' : 'false');
|
||||||
|
},
|
||||||
|
|
||||||
|
isBodyEnabled: function() {
|
||||||
|
if (!global.localStorage) return true;
|
||||||
|
const enabled = global.localStorage.getItem('notifications_body_enabled');
|
||||||
|
// default to true if the popups are enabled
|
||||||
|
if (enabled === null) return this.isEnabled();
|
||||||
|
return enabled === 'true';
|
||||||
|
},
|
||||||
|
|
||||||
setAudioEnabled: function(enable) {
|
setAudioEnabled: function(enable) {
|
||||||
if (!global.localStorage) return;
|
if (!global.localStorage) return;
|
||||||
global.localStorage.setItem('audio_notifications_enabled',
|
global.localStorage.setItem('audio_notifications_enabled',
|
||||||
|
|
|
@ -259,6 +259,11 @@ function textForPowerEvent(event) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function textForPinnedEvent(event) {
|
||||||
|
const senderName = event.getSender();
|
||||||
|
return _t("%(senderName)s changed the pinned messages for the room.", {senderName});
|
||||||
|
}
|
||||||
|
|
||||||
function textForWidgetEvent(event) {
|
function textForWidgetEvent(event) {
|
||||||
const senderName = event.getSender();
|
const senderName = event.getSender();
|
||||||
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent();
|
const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent();
|
||||||
|
@ -304,6 +309,7 @@ const stateHandlers = {
|
||||||
'm.room.history_visibility': textForHistoryVisibilityEvent,
|
'm.room.history_visibility': textForHistoryVisibilityEvent,
|
||||||
'm.room.encryption': textForEncryptionEvent,
|
'm.room.encryption': textForEncryptionEvent,
|
||||||
'm.room.power_levels': textForPowerEvent,
|
'm.room.power_levels': textForPowerEvent,
|
||||||
|
'm.room.pinned_events': textForPinnedEvent,
|
||||||
|
|
||||||
'im.vector.modular.widgets': textForWidgetEvent,
|
'im.vector.modular.widgets': textForWidgetEvent,
|
||||||
};
|
};
|
||||||
|
|
|
@ -30,6 +30,10 @@ const FEATURES = [
|
||||||
id: 'feature_groups',
|
id: 'feature_groups',
|
||||||
name: _td("Communities"),
|
name: _td("Communities"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'feature_pinning',
|
||||||
|
name: _td("Message Pinning"),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -98,6 +102,17 @@ export default {
|
||||||
Notifier.setEnabled(enable);
|
Notifier.setEnabled(enable);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getEnableNotificationBody: function() {
|
||||||
|
return Notifier.isBodyEnabled();
|
||||||
|
},
|
||||||
|
|
||||||
|
setEnableNotificationBody: function(enable) {
|
||||||
|
if (!Notifier.supportsDesktopNotifications()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Notifier.setBodyEnabled(enable);
|
||||||
|
},
|
||||||
|
|
||||||
getEnableAudioNotifications: function() {
|
getEnableAudioNotifications: function() {
|
||||||
return Notifier.isAudioEnabled();
|
return Notifier.isAudioEnabled();
|
||||||
},
|
},
|
||||||
|
|
|
@ -43,6 +43,10 @@ module.exports = React.createClass({
|
||||||
// the end of the live timeline.
|
// the end of the live timeline.
|
||||||
atEndOfLiveTimeline: React.PropTypes.bool,
|
atEndOfLiveTimeline: React.PropTypes.bool,
|
||||||
|
|
||||||
|
// This is true when the user is alone in the room, but has also sent a message.
|
||||||
|
// Used to suggest to the user to invite someone
|
||||||
|
sentMessageAndIsAlone: React.PropTypes.bool,
|
||||||
|
|
||||||
// true if there is an active call in this room (means we show
|
// true if there is an active call in this room (means we show
|
||||||
// the 'Active Call' text in the status bar if there is nothing
|
// the 'Active Call' text in the status bar if there is nothing
|
||||||
// more interesting)
|
// more interesting)
|
||||||
|
@ -60,6 +64,14 @@ module.exports = React.createClass({
|
||||||
// 'unsent messages' bar
|
// 'unsent messages' bar
|
||||||
onCancelAllClick: React.PropTypes.func,
|
onCancelAllClick: React.PropTypes.func,
|
||||||
|
|
||||||
|
// callback for when the user clicks on the 'invite others' button in the
|
||||||
|
// 'you are alone' bar
|
||||||
|
onInviteClick: React.PropTypes.func,
|
||||||
|
|
||||||
|
// callback for when the user clicks on the 'stop warning me' button in the
|
||||||
|
// 'you are alone' bar
|
||||||
|
onStopWarningClick: React.PropTypes.func,
|
||||||
|
|
||||||
// callback for when the user clicks on the 'scroll to bottom' button
|
// callback for when the user clicks on the 'scroll to bottom' button
|
||||||
onScrollToBottomClick: React.PropTypes.func,
|
onScrollToBottomClick: React.PropTypes.func,
|
||||||
|
|
||||||
|
@ -140,7 +152,8 @@ module.exports = React.createClass({
|
||||||
(this.state.usersTyping.length > 0) ||
|
(this.state.usersTyping.length > 0) ||
|
||||||
this.props.numUnreadMessages ||
|
this.props.numUnreadMessages ||
|
||||||
!this.props.atEndOfLiveTimeline ||
|
!this.props.atEndOfLiveTimeline ||
|
||||||
this.props.hasActiveCall
|
this.props.hasActiveCall ||
|
||||||
|
this.props.sentMessageAndIsAlone
|
||||||
) {
|
) {
|
||||||
return STATUS_BAR_EXPANDED;
|
return STATUS_BAR_EXPANDED;
|
||||||
} else if (this.props.unsentMessageError) {
|
} else if (this.props.unsentMessageError) {
|
||||||
|
@ -305,6 +318,21 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If you're alone in the room, and have sent a message, suggest to invite someone
|
||||||
|
if (this.props.sentMessageAndIsAlone) {
|
||||||
|
return (
|
||||||
|
<div className="mx_RoomStatusBar_isAlone">
|
||||||
|
{ _tJsx("There's no one else here! Would you like to <a>invite others</a> or <a>stop warning about the empty room</a>?",
|
||||||
|
[/<a>(.*?)<\/a>/, /<a>(.*?)<\/a>/],
|
||||||
|
[
|
||||||
|
(sub) => <a className="mx_RoomStatusBar_resend_link" key="invite" onClick={this.props.onInviteClick}>{ sub }</a>,
|
||||||
|
(sub) => <a className="mx_RoomStatusBar_resend_link" key="nowarn" onClick={this.props.onStopWarningClick}>{ sub }</a>,
|
||||||
|
],
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -117,6 +117,7 @@ module.exports = React.createClass({
|
||||||
guestsCanJoin: false,
|
guestsCanJoin: false,
|
||||||
canPeek: false,
|
canPeek: false,
|
||||||
showApps: false,
|
showApps: false,
|
||||||
|
isAlone: false,
|
||||||
isPeeking: false,
|
isPeeking: false,
|
||||||
|
|
||||||
// error object, as from the matrix client/server API
|
// error object, as from the matrix client/server API
|
||||||
|
@ -461,6 +462,8 @@ module.exports = React.createClass({
|
||||||
switch (payload.action) {
|
switch (payload.action) {
|
||||||
case 'message_send_failed':
|
case 'message_send_failed':
|
||||||
case 'message_sent':
|
case 'message_sent':
|
||||||
|
this._checkIfAlone(this.state.room);
|
||||||
|
// no break; to intentionally fall through
|
||||||
case 'message_send_cancelled':
|
case 'message_send_cancelled':
|
||||||
this.setState({
|
this.setState({
|
||||||
unsentMessageError: this._getUnsentMessageError(this.state.room),
|
unsentMessageError: this._getUnsentMessageError(this.state.room),
|
||||||
|
@ -740,6 +743,20 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
|
_checkIfAlone: function(room) {
|
||||||
|
let warnedAboutLonelyRoom = false;
|
||||||
|
if (localStorage) {
|
||||||
|
warnedAboutLonelyRoom = localStorage.getItem('mx_user_alone_warned_' + this.state.room.roomId);
|
||||||
|
}
|
||||||
|
if (warnedAboutLonelyRoom) {
|
||||||
|
if (this.state.isAlone) this.setState({isAlone: false});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinedMembers = room.currentState.getMembers().filter(m => m.membership === "join" || m.membership === "invite");
|
||||||
|
this.setState({isAlone: joinedMembers.length === 1});
|
||||||
|
},
|
||||||
|
|
||||||
_getUnsentMessageError: function(room) {
|
_getUnsentMessageError: function(room) {
|
||||||
const unsentMessages = this._getUnsentMessages(room);
|
const unsentMessages = this._getUnsentMessages(room);
|
||||||
if (!unsentMessages.length) return "";
|
if (!unsentMessages.length) return "";
|
||||||
|
@ -821,6 +838,22 @@ module.exports = React.createClass({
|
||||||
Resend.cancelUnsentEvents(this.state.room);
|
Resend.cancelUnsentEvents(this.state.room);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onInviteButtonClick: function() {
|
||||||
|
// call AddressPickerDialog
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_invite',
|
||||||
|
roomId: this.state.room.roomId,
|
||||||
|
});
|
||||||
|
this.setState({isAlone: false}); // there's a good chance they'll invite someone
|
||||||
|
},
|
||||||
|
|
||||||
|
onStopAloneWarningClick: function() {
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem('mx_user_alone_warned_' + this.state.room.roomId, true);
|
||||||
|
}
|
||||||
|
this.setState({isAlone: false});
|
||||||
|
},
|
||||||
|
|
||||||
onJoinButtonClicked: function(ev) {
|
onJoinButtonClicked: function(ev) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
@ -1144,6 +1177,10 @@ module.exports = React.createClass({
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onPinnedClick: function() {
|
||||||
|
this.setState({showingPinned: !this.state.showingPinned, searching: false});
|
||||||
|
},
|
||||||
|
|
||||||
onSettingsClick: function() {
|
onSettingsClick: function() {
|
||||||
this.showSettings(true);
|
this.showSettings(true);
|
||||||
},
|
},
|
||||||
|
@ -1263,7 +1300,7 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onSearchClick: function() {
|
onSearchClick: function() {
|
||||||
this.setState({ searching: true });
|
this.setState({ searching: true, showingPinned: false });
|
||||||
},
|
},
|
||||||
|
|
||||||
onCancelSearchClick: function() {
|
onCancelSearchClick: function() {
|
||||||
|
@ -1462,6 +1499,7 @@ module.exports = React.createClass({
|
||||||
const RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
const RoomSettings = sdk.getComponent("rooms.RoomSettings");
|
||||||
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
const AuxPanel = sdk.getComponent("rooms.AuxPanel");
|
||||||
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
const SearchBar = sdk.getComponent("rooms.SearchBar");
|
||||||
|
const PinnedEventsPanel = sdk.getComponent("rooms.PinnedEventsPanel");
|
||||||
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
const ScrollPanel = sdk.getComponent("structures.ScrollPanel");
|
||||||
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
const TintableSvg = sdk.getComponent("elements.TintableSvg");
|
||||||
const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
const RoomPreviewBar = sdk.getComponent("rooms.RoomPreviewBar");
|
||||||
|
@ -1581,9 +1619,12 @@ module.exports = React.createClass({
|
||||||
numUnreadMessages={this.state.numUnreadMessages}
|
numUnreadMessages={this.state.numUnreadMessages}
|
||||||
unsentMessageError={this.state.unsentMessageError}
|
unsentMessageError={this.state.unsentMessageError}
|
||||||
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
atEndOfLiveTimeline={this.state.atEndOfLiveTimeline}
|
||||||
|
sentMessageAndIsAlone={this.state.isAlone}
|
||||||
hasActiveCall={inCall}
|
hasActiveCall={inCall}
|
||||||
onResendAllClick={this.onResendAllClick}
|
onResendAllClick={this.onResendAllClick}
|
||||||
onCancelAllClick={this.onCancelAllClick}
|
onCancelAllClick={this.onCancelAllClick}
|
||||||
|
onInviteClick={this.onInviteButtonClick}
|
||||||
|
onStopWarningClick={this.onStopAloneWarningClick}
|
||||||
onScrollToBottomClick={this.jumpToLiveTimeline}
|
onScrollToBottomClick={this.jumpToLiveTimeline}
|
||||||
onResize={this.onChildResize}
|
onResize={this.onChildResize}
|
||||||
onVisible={this.onStatusBarVisible}
|
onVisible={this.onStatusBarVisible}
|
||||||
|
@ -1603,6 +1644,9 @@ module.exports = React.createClass({
|
||||||
} else if (this.state.searching) {
|
} else if (this.state.searching) {
|
||||||
hideCancel = true; // has own cancel
|
hideCancel = true; // has own cancel
|
||||||
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />;
|
aux = <SearchBar ref="search_bar" searchInProgress={this.state.searchInProgress} onCancelClick={this.onCancelSearchClick} onSearch={this.onSearch} />;
|
||||||
|
} else if (this.state.showingPinned) {
|
||||||
|
hideCancel = true; // has own cancel
|
||||||
|
aux = <PinnedEventsPanel room={this.state.room} onCancelClick={this.onPinnedClick} />;
|
||||||
} else if (!myMember || myMember.membership !== "join") {
|
} else if (!myMember || myMember.membership !== "join") {
|
||||||
// We do have a room object for this room, but we're not currently in it.
|
// We do have a room object for this room, but we're not currently in it.
|
||||||
// We may have a 3rd party invite to it.
|
// We may have a 3rd party invite to it.
|
||||||
|
@ -1776,6 +1820,7 @@ module.exports = React.createClass({
|
||||||
collapsedRhs={this.props.collapsedRhs}
|
collapsedRhs={this.props.collapsedRhs}
|
||||||
onSearchClick={this.onSearchClick}
|
onSearchClick={this.onSearchClick}
|
||||||
onSettingsClick={this.onSettingsClick}
|
onSettingsClick={this.onSettingsClick}
|
||||||
|
onPinnedClick={this.onPinnedClick}
|
||||||
onSaveClick={this.onSettingsSaveClick}
|
onSaveClick={this.onSettingsSaveClick}
|
||||||
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
onCancelClick={(aux && !hideCancel) ? this.onCancelClick : null}
|
||||||
onForgetClick={(myMember && myMember.membership === "leave") ? this.onForgetClick : null}
|
onForgetClick={(myMember && myMember.membership === "leave") ? this.onForgetClick : null}
|
||||||
|
|
|
@ -114,6 +114,10 @@ const SETTINGS_LABELS = [
|
||||||
id: 'Pill.shouldHidePillAvatar',
|
id: 'Pill.shouldHidePillAvatar',
|
||||||
label: _td('Hide avatars in user and room mentions'),
|
label: _td('Hide avatars in user and room mentions'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'TextualBody.disableBigEmoji',
|
||||||
|
label: _td('Disable big emoji in chat'),
|
||||||
|
},
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
id: 'useFixedWidthFont',
|
id: 'useFixedWidthFont',
|
||||||
|
@ -423,6 +427,11 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onAvatarRemoveClick: function() {
|
||||||
|
MatrixClientPeg.get().setAvatarUrl(null);
|
||||||
|
this.setState({avatarUrl: null}); // the avatar update will complete async for us
|
||||||
|
},
|
||||||
|
|
||||||
onLogoutClicked: function(ev) {
|
onLogoutClicked: function(ev) {
|
||||||
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
|
||||||
Modal.createTrackedDialog('Logout E2E Export', '', QuestionDialog, {
|
Modal.createTrackedDialog('Logout E2E Export', '', QuestionDialog, {
|
||||||
|
@ -1318,7 +1327,11 @@ module.exports = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_UserSettings_avatarPicker">
|
<div className="mx_UserSettings_avatarPicker">
|
||||||
<div onClick={this.onAvatarPickerClick}>
|
<div className="mx_UserSettings_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
||||||
|
<img src="img/cancel.svg" width="15" height="15"
|
||||||
|
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
|
||||||
|
</div>
|
||||||
|
<div onClick={this.onAvatarPickerClick} className="mx_UserSettings_avatarPicker_imgContainer">
|
||||||
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
|
<ChangeAvatar ref="changeAvatar" initialAvatarUrl={avatarUrl}
|
||||||
showUploadSection={false} className="mx_UserSettings_avatarPicker_img" />
|
showUploadSection={false} className="mx_UserSettings_avatarPicker_img" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -35,7 +35,9 @@ export default withMatrixClient(React.createClass({
|
||||||
getInitialState: function() {
|
getInitialState: function() {
|
||||||
return {
|
return {
|
||||||
fetching: false,
|
fetching: false,
|
||||||
|
fetchingInvitedMembers: false,
|
||||||
members: null,
|
members: null,
|
||||||
|
invitedMembers: null,
|
||||||
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
truncateAt: INITIAL_LOAD_NUM_MEMBERS,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -46,7 +48,10 @@ export default withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_fetchMembers: function() {
|
_fetchMembers: function() {
|
||||||
this.setState({fetching: true});
|
this.setState({
|
||||||
|
fetching: true,
|
||||||
|
fetchingInvitedMembers: true,
|
||||||
|
});
|
||||||
this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
|
this.props.matrixClient.getGroupUsers(this.props.groupId).then((result) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
members: result.chunk.map((apiMember) => {
|
members: result.chunk.map((apiMember) => {
|
||||||
|
@ -58,6 +63,18 @@ export default withMatrixClient(React.createClass({
|
||||||
this.setState({fetching: false});
|
this.setState({fetching: false});
|
||||||
console.error("Failed to get group member list: " + e);
|
console.error("Failed to get group member list: " + e);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.props.matrixClient.getGroupInvitedUsers(this.props.groupId).then((result) => {
|
||||||
|
this.setState({
|
||||||
|
invitedMembers: result.chunk.map((apiMember) => {
|
||||||
|
return groupMemberFromApiObject(apiMember);
|
||||||
|
}),
|
||||||
|
fetchingInvitedMembers: false,
|
||||||
|
});
|
||||||
|
}).catch((e) => {
|
||||||
|
this.setState({fetchingInvitedMembers: false});
|
||||||
|
console.error("Failed to get group invited member list: " + e);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_createOverflowTile: function(overflowCount, totalCount) {
|
_createOverflowTile: function(overflowCount, totalCount) {
|
||||||
|
@ -83,11 +100,10 @@ export default withMatrixClient(React.createClass({
|
||||||
this.setState({ searchQuery: ev.target.value });
|
this.setState({ searchQuery: ev.target.value });
|
||||||
},
|
},
|
||||||
|
|
||||||
makeGroupMemberTiles: function(query) {
|
makeGroupMemberTiles: function(query, memberList) {
|
||||||
const GroupMemberTile = sdk.getComponent("groups.GroupMemberTile");
|
const GroupMemberTile = sdk.getComponent("groups.GroupMemberTile");
|
||||||
|
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
||||||
query = (query || "").toLowerCase();
|
query = (query || "").toLowerCase();
|
||||||
|
|
||||||
let memberList = this.state.members;
|
|
||||||
if (query) {
|
if (query) {
|
||||||
memberList = memberList.filter((m) => {
|
memberList = memberList.filter((m) => {
|
||||||
const matchesName = m.displayname.toLowerCase().indexOf(query) !== -1;
|
const matchesName = m.displayname.toLowerCase().indexOf(query) !== -1;
|
||||||
|
@ -118,17 +134,19 @@ export default withMatrixClient(React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return memberList;
|
return <TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
|
||||||
|
createOverflowElement={this._createOverflowTile}
|
||||||
|
>
|
||||||
|
{ memberList }
|
||||||
|
</TruncatedList>;
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
if (this.state.fetching) {
|
if (this.state.fetching || this.state.fetchingInvitedMembers) {
|
||||||
const Spinner = sdk.getComponent("elements.Spinner");
|
const Spinner = sdk.getComponent("elements.Spinner");
|
||||||
return (<div className="mx_MemberList">
|
return (<div className="mx_MemberList">
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</div>);
|
</div>);
|
||||||
} else if (this.state.members === null) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inputBox = (
|
const inputBox = (
|
||||||
|
@ -139,15 +157,21 @@ export default withMatrixClient(React.createClass({
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
|
|
||||||
const TruncatedList = sdk.getComponent("elements.TruncatedList");
|
const joined = this.state.members ? <div className="mx_MemberList_joined">
|
||||||
|
{ this.makeGroupMemberTiles(this.state.searchQuery, this.state.members) }
|
||||||
|
</div> : <div />;
|
||||||
|
|
||||||
|
const invited = this.state.invitedMembers ? <div className="mx_MemberList_invited">
|
||||||
|
<h2>{ _t("Invited") }</h2>
|
||||||
|
{ this.makeGroupMemberTiles(this.state.searchQuery, this.state.invitedMembers) }
|
||||||
|
</div> : <div />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx_MemberList">
|
<div className="mx_MemberList">
|
||||||
{ inputBox }
|
{ inputBox }
|
||||||
<GeminiScrollbar autoshow={true} className="mx_MemberList_joined mx_MemberList_outerWrapper">
|
<GeminiScrollbar autoshow={true} className="mx_MemberList_outerWrapper">
|
||||||
<TruncatedList className="mx_MemberList_wrapper" truncateAt={this.state.truncateAt}
|
{ joined }
|
||||||
createOverflowElement={this._createOverflowTile}>
|
{ invited }
|
||||||
{ this.makeGroupMemberTiles(this.state.searchQuery) }
|
|
||||||
</TruncatedList>
|
|
||||||
</GeminiScrollbar>
|
</GeminiScrollbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -70,9 +70,9 @@ module.exports = React.createClass({
|
||||||
// it sucks that _tJsx doesn't support normal _t substitutions :((
|
// it sucks that _tJsx doesn't support normal _t substitutions :((
|
||||||
return (
|
return (
|
||||||
<div className="mx_RoomAvatarEvent">
|
<div className="mx_RoomAvatarEvent">
|
||||||
{ _tJsx('$senderDisplayName changed the room avatar to <img/>',
|
{ _tJsx('%(senderDisplayName)s changed the room avatar to <img/>',
|
||||||
[
|
[
|
||||||
/\$senderDisplayName/,
|
/%\(senderDisplayName\)s/,
|
||||||
/<img\/>/,
|
/<img\/>/,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
@ -354,7 +354,9 @@ module.exports = React.createClass({
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
const content = mxEvent.getContent();
|
const content = mxEvent.getContent();
|
||||||
|
|
||||||
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {});
|
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
|
||||||
|
disableBigEmoji: UserSettingsStore.getSyncedSetting('TextualBody.disableBigEmoji', false),
|
||||||
|
});
|
||||||
|
|
||||||
if (this.props.highlightLink) {
|
if (this.props.highlightLink) {
|
||||||
body = <a href={this.props.highlightLink}>{ body }</a>;
|
body = <a href={this.props.highlightLink}>{ body }</a>;
|
||||||
|
|
|
@ -34,7 +34,7 @@ const ROOM_COLORS = [
|
||||||
["#dad658", "#f5f4ea"],
|
["#dad658", "#f5f4ea"],
|
||||||
["#80c553", "#eef5ea"],
|
["#80c553", "#eef5ea"],
|
||||||
["#bb814e", "#eee8e3"],
|
["#bb814e", "#eee8e3"],
|
||||||
["#595959", "#ececec"],
|
//["#595959", "#ececec"], // Grey makes everything appear disabled, so remove it for now
|
||||||
];
|
];
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
|
|
|
@ -116,7 +116,9 @@ module.exports = React.createClass({
|
||||||
nameEl = (
|
nameEl = (
|
||||||
<div className="mx_EntityTile_details">
|
<div className="mx_EntityTile_details">
|
||||||
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12" />
|
<img className="mx_EntityTile_chevron" src="img/member_chevron.png" width="8" height="12" />
|
||||||
<EmojiText element="div" className="mx_EntityTile_name_hover" dir="auto">{ name }</EmojiText>
|
<EmojiText element="div" className="mx_EntityTile_name mx_EntityTile_name_hover" dir="auto">
|
||||||
|
{ name }
|
||||||
|
</EmojiText>
|
||||||
<PresenceLabel activeAgo={activeAgo}
|
<PresenceLabel activeAgo={activeAgo}
|
||||||
currentlyActive={this.props.presenceCurrentlyActive}
|
currentlyActive={this.props.presenceCurrentlyActive}
|
||||||
presenceState={this.props.presenceState} />
|
presenceState={this.props.presenceState} />
|
||||||
|
|
|
@ -44,6 +44,7 @@ const eventTileTypes = {
|
||||||
'm.room.history_visibility': 'messages.TextualEvent',
|
'm.room.history_visibility': 'messages.TextualEvent',
|
||||||
'm.room.encryption': 'messages.TextualEvent',
|
'm.room.encryption': 'messages.TextualEvent',
|
||||||
'm.room.power_levels': 'messages.TextualEvent',
|
'm.room.power_levels': 'messages.TextualEvent',
|
||||||
|
'm.room.pinned_events' : 'messages.TextualEvent',
|
||||||
|
|
||||||
'im.vector.modular.widgets': 'messages.TextualEvent',
|
'im.vector.modular.widgets': 'messages.TextualEvent',
|
||||||
};
|
};
|
||||||
|
|
|
@ -625,22 +625,49 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
_renderUserOptions: function() {
|
_renderUserOptions: function() {
|
||||||
// Only allow the user to ignore the user if its not ourselves
|
const cli = this.props.matrixClient;
|
||||||
|
const member = this.props.member;
|
||||||
|
|
||||||
let ignoreButton = null;
|
let ignoreButton = null;
|
||||||
if (this.props.member.userId !== this.props.matrixClient.getUserId()) {
|
let readReceiptButton = null;
|
||||||
|
|
||||||
|
// Only allow the user to ignore the user if its not ourselves
|
||||||
|
// same goes for jumping to read receipt
|
||||||
|
if (member.userId !== cli.getUserId()) {
|
||||||
ignoreButton = (
|
ignoreButton = (
|
||||||
<AccessibleButton onClick={this.onIgnoreToggle} className="mx_MemberInfo_field">
|
<AccessibleButton onClick={this.onIgnoreToggle} className="mx_MemberInfo_field">
|
||||||
{ this.state.isIgnoring ? _t("Unignore") : _t("Ignore") }
|
{ this.state.isIgnoring ? _t("Unignore") : _t("Ignore") }
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (member.roomId) {
|
||||||
|
const room = cli.getRoom(member.roomId);
|
||||||
|
const eventId = room.getEventReadUpTo(member.userId);
|
||||||
|
|
||||||
|
const onReadReceiptButton = function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
highlighted: true,
|
||||||
|
event_id: eventId,
|
||||||
|
room_id: member.roomId,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
readReceiptButton = (
|
||||||
|
<AccessibleButton onClick={onReadReceiptButton} className="mx_MemberInfo_field">
|
||||||
|
{ _t('Jump to read receipt') }
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignoreButton) return null;
|
if (!ignoreButton && !readReceiptButton) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>{ _t("User Options") }</h3>
|
<h3>{ _t("User Options") }</h3>
|
||||||
<div className="mx_MemberInfo_buttons">
|
<div className="mx_MemberInfo_buttons">
|
||||||
|
{ readReceiptButton }
|
||||||
{ ignoreButton }
|
{ ignoreButton }
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -146,8 +146,8 @@ module.exports = React.createClass({
|
||||||
const newState = {
|
const newState = {
|
||||||
members: this.roomMembers(),
|
members: this.roomMembers(),
|
||||||
};
|
};
|
||||||
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join');
|
newState.filteredJoinedMembers = this._filterMembers(newState.members, 'join', this.state.searchQuery);
|
||||||
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite');
|
newState.filteredInvitedMembers = this._filterMembers(newState.members, 'invite', this.state.searchQuery);
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
}, 500),
|
}, 500),
|
||||||
|
|
||||||
|
@ -187,7 +187,7 @@ module.exports = React.createClass({
|
||||||
const user_id = all_user_ids[i];
|
const user_id = all_user_ids[i];
|
||||||
const m = all_members[user_id];
|
const m = all_members[user_id];
|
||||||
|
|
||||||
if (m.membership == 'join' || m.membership == 'invite') {
|
if (m.membership === 'join' || m.membership === 'invite') {
|
||||||
if ((ConferenceHandler && !ConferenceHandler.isConferenceUser(user_id)) || !ConferenceHandler) {
|
if ((ConferenceHandler && !ConferenceHandler.isConferenceUser(user_id)) || !ConferenceHandler) {
|
||||||
to_display.push(user_id);
|
to_display.push(user_id);
|
||||||
++count;
|
++count;
|
||||||
|
@ -302,6 +302,7 @@ module.exports = React.createClass({
|
||||||
const m = this.memberDict[userId];
|
const m = this.memberDict[userId];
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
|
query = query.toLowerCase();
|
||||||
const matchesName = m.name.toLowerCase().indexOf(query) !== -1;
|
const matchesName = m.name.toLowerCase().indexOf(query) !== -1;
|
||||||
const matchesId = m.userId.toLowerCase().indexOf(query) !== -1;
|
const matchesId = m.userId.toLowerCase().indexOf(query) !== -1;
|
||||||
|
|
||||||
|
@ -310,7 +311,7 @@ module.exports = React.createClass({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.membership == membership;
|
return m.membership === membership;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
90
src/components/views/rooms/PinnedEventTile.js
Normal file
90
src/components/views/rooms/PinnedEventTile.js
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 Travis Ralston
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
import dis from "../../../dispatcher";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import MessageEvent from "../messages/MessageEvent";
|
||||||
|
import MemberAvatar from "../avatars/MemberAvatar";
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'PinnedEventTile',
|
||||||
|
propTypes: {
|
||||||
|
mxRoom: React.PropTypes.object.isRequired,
|
||||||
|
mxEvent: React.PropTypes.object.isRequired,
|
||||||
|
onUnpinned: React.PropTypes.func,
|
||||||
|
},
|
||||||
|
onTileClicked: function() {
|
||||||
|
dis.dispatch({
|
||||||
|
action: 'view_room',
|
||||||
|
event_id: this.props.mxEvent.getId(),
|
||||||
|
highlighted: true,
|
||||||
|
room_id: this.props.mxEvent.getRoomId(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onUnpinClicked: function() {
|
||||||
|
const pinnedEvents = this.props.mxRoom.currentState.getStateEvents("m.room.pinned_events", "");
|
||||||
|
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
||||||
|
// Nothing to do: already unpinned
|
||||||
|
if (this.props.onUnpinned) this.props.onUnpinned();
|
||||||
|
} else {
|
||||||
|
const pinned = pinnedEvents.getContent().pinned;
|
||||||
|
const index = pinned.indexOf(this.props.mxEvent.getId());
|
||||||
|
if (index !== -1) {
|
||||||
|
pinned.splice(index, 1);
|
||||||
|
MatrixClientPeg.get().sendStateEvent(this.props.mxRoom.roomId, 'm.room.pinned_events', {pinned}, '')
|
||||||
|
.then(() => {
|
||||||
|
if (this.props.onUnpinned) this.props.onUnpinned();
|
||||||
|
});
|
||||||
|
} else if (this.props.onUnpinned) this.props.onUnpinned();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_canUnpin: function() {
|
||||||
|
return this.props.mxRoom.currentState.mayClientSendStateEvent('m.room.pinned_events', MatrixClientPeg.get());
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
const sender = this.props.mxRoom.getMember(this.props.mxEvent.getSender());
|
||||||
|
const avatarSize = 40;
|
||||||
|
|
||||||
|
let unpinButton = null;
|
||||||
|
if (this._canUnpin()) {
|
||||||
|
unpinButton = (
|
||||||
|
<AccessibleButton onClick={this.onUnpinClicked} className="mx_PinnedEventTile_unpinButton">
|
||||||
|
<img src="img/cancel-red.svg" width="8" height="8" alt={_t('Unpin Message')} title={_t('Unpin Message')} />
|
||||||
|
</AccessibleButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_PinnedEventTile">
|
||||||
|
<div className="mx_PinnedEventTile_actions">
|
||||||
|
<AccessibleButton className="mx_PinnedEventTile_gotoButton mx_textButton" onClick={this.onTileClicked}>
|
||||||
|
{ _t("Jump to message") }
|
||||||
|
</AccessibleButton>
|
||||||
|
{ unpinButton }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MemberAvatar member={sender} width={avatarSize} height={avatarSize} />
|
||||||
|
<span className="mx_PinnedEventTile_sender">
|
||||||
|
{ sender.name }
|
||||||
|
</span>
|
||||||
|
<MessageEvent mxEvent={this.props.mxEvent} className="mx_PinnedEventTile_body" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
105
src/components/views/rooms/PinnedEventsPanel.js
Normal file
105
src/components/views/rooms/PinnedEventsPanel.js
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
/*
|
||||||
|
Copyright 2017 Travis Ralston
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
import AccessibleButton from "../elements/AccessibleButton";
|
||||||
|
import PinnedEventTile from "./PinnedEventTile";
|
||||||
|
import { _t } from '../../../languageHandler';
|
||||||
|
|
||||||
|
module.exports = React.createClass({
|
||||||
|
displayName: 'PinnedEventsPanel',
|
||||||
|
propTypes: {
|
||||||
|
// The Room from the js-sdk we're going to show pinned events for
|
||||||
|
room: React.PropTypes.object.isRequired,
|
||||||
|
|
||||||
|
onCancelClick: React.PropTypes.func,
|
||||||
|
},
|
||||||
|
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount: function() {
|
||||||
|
this._updatePinnedMessages();
|
||||||
|
},
|
||||||
|
|
||||||
|
_updatePinnedMessages: function() {
|
||||||
|
const pinnedEvents = this.props.room.currentState.getStateEvents("m.room.pinned_events", "");
|
||||||
|
if (!pinnedEvents || !pinnedEvents.getContent().pinned) {
|
||||||
|
this.setState({ loading: false, pinned: [] });
|
||||||
|
} else {
|
||||||
|
const promises = [];
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
|
||||||
|
pinnedEvents.getContent().pinned.map((eventId) => {
|
||||||
|
promises.push(cli.getEventTimeline(this.props.room.getUnfilteredTimelineSet(), eventId, 0).then(
|
||||||
|
(timeline) => {
|
||||||
|
const event = timeline.getEvents().find((e) => e.getId() === eventId);
|
||||||
|
return {eventId, timeline, event};
|
||||||
|
}).catch((err) => {
|
||||||
|
console.error("Error looking up pinned event " + eventId + " in room " + this.props.room.roomId);
|
||||||
|
console.error(err);
|
||||||
|
return null; // return lack of context to avoid unhandled errors
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
Promise.all(promises).then((contexts) => {
|
||||||
|
// Filter out the messages before we try to render them
|
||||||
|
const pinned = contexts.filter((context) => {
|
||||||
|
if (!context) return false; // no context == not applicable for the room
|
||||||
|
if (context.event.getType() !== "m.room.message") return false;
|
||||||
|
if (context.event.isRedacted()) return false;
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({ loading: false, pinned });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPinnedTiles: function() {
|
||||||
|
if (this.state.pinned.length == 0) {
|
||||||
|
return (<div>{ _t("No pinned messages.") }</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.state.pinned.map((context) => {
|
||||||
|
return (<PinnedEventTile key={context.event.getId()}
|
||||||
|
mxRoom={this.props.room}
|
||||||
|
mxEvent={context.event}
|
||||||
|
onUnpinned={this._updatePinnedMessages} />);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
let tiles = <div>{ _t("Loading...") }</div>;
|
||||||
|
if (this.state && !this.state.loading) {
|
||||||
|
tiles = this._getPinnedTiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mx_PinnedEventsPanel">
|
||||||
|
<div className="mx_PinnedEventsPanel_body">
|
||||||
|
<AccessibleButton className="mx_PinnedEventsPanel_cancel" onClick={this.props.onCancelClick}><img src="img/cancel.svg" width="18" height="18" /></AccessibleButton>
|
||||||
|
<h3 className="mx_PinnedEventsPanel_header">{ _t("Pinned Messages") }</h3>
|
||||||
|
{ tiles }
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
|
@ -31,6 +31,7 @@ import linkifyMatrix from '../../../linkify-matrix';
|
||||||
import AccessibleButton from '../elements/AccessibleButton';
|
import AccessibleButton from '../elements/AccessibleButton';
|
||||||
import ManageIntegsButton from '../elements/ManageIntegsButton';
|
import ManageIntegsButton from '../elements/ManageIntegsButton';
|
||||||
import {CancelButton} from './SimpleRoomHeader';
|
import {CancelButton} from './SimpleRoomHeader';
|
||||||
|
import UserSettingsStore from "../../../UserSettingsStore";
|
||||||
|
|
||||||
linkifyMatrix(linkify);
|
linkifyMatrix(linkify);
|
||||||
|
|
||||||
|
@ -45,6 +46,7 @@ module.exports = React.createClass({
|
||||||
inRoom: React.PropTypes.bool,
|
inRoom: React.PropTypes.bool,
|
||||||
collapsedRhs: React.PropTypes.bool,
|
collapsedRhs: React.PropTypes.bool,
|
||||||
onSettingsClick: React.PropTypes.func,
|
onSettingsClick: React.PropTypes.func,
|
||||||
|
onPinnedClick: React.PropTypes.func,
|
||||||
onSaveClick: React.PropTypes.func,
|
onSaveClick: React.PropTypes.func,
|
||||||
onSearchClick: React.PropTypes.func,
|
onSearchClick: React.PropTypes.func,
|
||||||
onLeaveClick: React.PropTypes.func,
|
onLeaveClick: React.PropTypes.func,
|
||||||
|
@ -129,6 +131,10 @@ module.exports = React.createClass({
|
||||||
}).done();
|
}).done();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onAvatarRemoveClick: function() {
|
||||||
|
MatrixClientPeg.get().sendStateEvent(this.props.room.roomId, 'm.room.avatar', {url: null}, '');
|
||||||
|
},
|
||||||
|
|
||||||
onShowRhsClick: function(ev) {
|
onShowRhsClick: function(ev) {
|
||||||
dis.dispatch({ action: 'show_right_panel' });
|
dis.dispatch({ action: 'show_right_panel' });
|
||||||
},
|
},
|
||||||
|
@ -172,6 +178,7 @@ module.exports = React.createClass({
|
||||||
let spinner = null;
|
let spinner = null;
|
||||||
let saveButton = null;
|
let saveButton = null;
|
||||||
let settingsButton = null;
|
let settingsButton = null;
|
||||||
|
let pinnedEventsButton = null;
|
||||||
|
|
||||||
let canSetRoomName;
|
let canSetRoomName;
|
||||||
let canSetRoomAvatar;
|
let canSetRoomAvatar;
|
||||||
|
@ -273,6 +280,10 @@ module.exports = React.createClass({
|
||||||
</label>
|
</label>
|
||||||
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
|
<input id="avatarInput" type="file" onChange={this.onAvatarSelected} />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mx_RoomHeader_avatarPicker_remove" onClick={this.onAvatarRemoveClick}>
|
||||||
|
<img src="img/cancel.svg" width="10"
|
||||||
|
alt={_t("Remove avatar")} title={_t("Remove avatar")} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (this.props.room || (this.props.oobData && this.props.oobData.name)) {
|
} else if (this.props.room || (this.props.oobData && this.props.oobData.name)) {
|
||||||
|
@ -290,6 +301,13 @@ module.exports = React.createClass({
|
||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.props.onPinnedClick && UserSettingsStore.isFeatureEnabled('feature_pinning')) {
|
||||||
|
pinnedEventsButton =
|
||||||
|
<AccessibleButton className="mx_RoomHeader_button" onClick={this.props.onPinnedClick} title={_t("Pinned Messages")}>
|
||||||
|
<TintableSvg src="img/icons-pin.svg" width="16" height="16" />
|
||||||
|
</AccessibleButton>;
|
||||||
|
}
|
||||||
|
|
||||||
// var leave_button;
|
// var leave_button;
|
||||||
// if (this.props.onLeaveClick) {
|
// if (this.props.onLeaveClick) {
|
||||||
// leave_button =
|
// leave_button =
|
||||||
|
@ -334,6 +352,7 @@ module.exports = React.createClass({
|
||||||
rightRow =
|
rightRow =
|
||||||
<div className="mx_RoomHeader_rightRow">
|
<div className="mx_RoomHeader_rightRow">
|
||||||
{ settingsButton }
|
{ settingsButton }
|
||||||
|
{ pinnedEventsButton }
|
||||||
{ manageIntegsButton }
|
{ manageIntegsButton }
|
||||||
{ forgetButton }
|
{ forgetButton }
|
||||||
{ searchButton }
|
{ searchButton }
|
||||||
|
|
|
@ -53,6 +53,10 @@ module.exports = React.createClass({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillMount: function() {
|
||||||
|
MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents);
|
||||||
|
},
|
||||||
|
|
||||||
componentWillReceiveProps: function(newProps) {
|
componentWillReceiveProps: function(newProps) {
|
||||||
if (this.avatarSet) {
|
if (this.avatarSet) {
|
||||||
// don't clobber what the user has just set
|
// don't clobber what the user has just set
|
||||||
|
@ -63,6 +67,28 @@ module.exports = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentWillUnmount: function() {
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
|
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRoomStateEvents: function(ev) {
|
||||||
|
if (!this.props.room) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ev.getRoomId() !== this.props.room.roomId || ev.getType() !== 'm.room.avatar'
|
||||||
|
|| ev.getSender() !== MatrixClientPeg.get().getUserId()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ev.getContent().url) {
|
||||||
|
this.avatarSet = false;
|
||||||
|
this.setState({}); // force update
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
setAvatarFromFile: function(file) {
|
setAvatarFromFile: function(file) {
|
||||||
let newUrl = null;
|
let newUrl = null;
|
||||||
|
|
||||||
|
|
|
@ -728,7 +728,7 @@
|
||||||
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNUNG: SCHLÜSSEL-VERIFIZIERUNG FEHLGESCHLAGEN! Der Signatur-Schlüssel für %(userId)s und das Gerät %(deviceId)s ist \"%(fprint)s\", welcher nicht mit dem bereitgestellten Schlüssel \"%(fingerprint)s\" übereinstimmt. Dies kann bedeuten, dass deine Kommunikation abgehört wird!",
|
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNUNG: SCHLÜSSEL-VERIFIZIERUNG FEHLGESCHLAGEN! Der Signatur-Schlüssel für %(userId)s und das Gerät %(deviceId)s ist \"%(fprint)s\", welcher nicht mit dem bereitgestellten Schlüssel \"%(fingerprint)s\" übereinstimmt. Dies kann bedeuten, dass deine Kommunikation abgehört wird!",
|
||||||
"You have <a>disabled</a> URL previews by default.": "Du hast die URL-Vorschau standardmäßig <a>deaktiviert</a>.",
|
"You have <a>disabled</a> URL previews by default.": "Du hast die URL-Vorschau standardmäßig <a>deaktiviert</a>.",
|
||||||
"You have <a>enabled</a> URL previews by default.": "Du hast die URL-Vorschau standardmäßig <a>aktiviert</a>.",
|
"You have <a>enabled</a> URL previews by default.": "Du hast die URL-Vorschau standardmäßig <a>aktiviert</a>.",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName hat das Raum-Bild geändert zu <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s hat das Raum-Bild geändert zu <img/>",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s hat das Raum-Bild für %(roomName)s geändert",
|
||||||
"Hide removed messages": "Gelöschte Nachrichten verbergen",
|
"Hide removed messages": "Gelöschte Nachrichten verbergen",
|
||||||
"Start new chat": "Neuen Chat starten",
|
"Start new chat": "Neuen Chat starten",
|
||||||
|
|
|
@ -639,7 +639,7 @@
|
||||||
"Disable URL previews by default for participants in this room": "Απενεργοποίηση της προεπισκόπησης συνδέσμων για όλους τους συμμετέχοντες στο δωμάτιο",
|
"Disable URL previews by default for participants in this room": "Απενεργοποίηση της προεπισκόπησης συνδέσμων για όλους τους συμμετέχοντες στο δωμάτιο",
|
||||||
"Disable URL previews for this room (affects only you)": "Απενεργοποίηση της προεπισκόπησης συνδέσμων για αυτό το δωμάτιο (επηρεάζει μόνο εσάς)",
|
"Disable URL previews for this room (affects only you)": "Απενεργοποίηση της προεπισκόπησης συνδέσμων για αυτό το δωμάτιο (επηρεάζει μόνο εσάς)",
|
||||||
" (unsupported)": " (μη υποστηριζόμενο)",
|
" (unsupported)": " (μη υποστηριζόμενο)",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "Ο $senderDisplayName άλλαξε την εικόνα του δωματίου σε <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "Ο %(senderDisplayName)s άλλαξε την εικόνα του δωματίου σε <img/>",
|
||||||
"Missing Media Permissions, click here to request.": "Λείπουν τα δικαιώματα πολύμεσων, κάντε κλικ για να ζητήσετε.",
|
"Missing Media Permissions, click here to request.": "Λείπουν τα δικαιώματα πολύμεσων, κάντε κλικ για να ζητήσετε.",
|
||||||
"You may need to manually permit Riot to access your microphone/webcam": "Μπορεί να χρειαστεί να ορίσετε χειροκίνητα την πρόσβαση του Riot στο μικρόφωνο/κάμερα",
|
"You may need to manually permit Riot to access your microphone/webcam": "Μπορεί να χρειαστεί να ορίσετε χειροκίνητα την πρόσβαση του Riot στο μικρόφωνο/κάμερα",
|
||||||
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Δεν είναι δυνατή η σύνδεση στον διακομιστή - παρακαλούμε ελέγξτε την συνδεσιμότητα, βεβαιωθείτε ότι το <a>πιστοποιητικό SSL</a> του διακομιστή είναι έμπιστο και ότι κάποιο πρόσθετο περιηγητή δεν αποτρέπει τα αιτήματα.",
|
"Can't connect to homeserver - please check your connectivity, ensure your <a>homeserver's SSL certificate</a> is trusted, and that a browser extension is not blocking requests.": "Δεν είναι δυνατή η σύνδεση στον διακομιστή - παρακαλούμε ελέγξτε την συνδεσιμότητα, βεβαιωθείτε ότι το <a>πιστοποιητικό SSL</a> του διακομιστή είναι έμπιστο και ότι κάποιο πρόσθετο περιηγητή δεν αποτρέπει τα αιτήματα.",
|
||||||
|
|
|
@ -252,6 +252,7 @@
|
||||||
"%(targetName)s joined the room.": "%(targetName)s joined the room.",
|
"%(targetName)s joined the room.": "%(targetName)s joined the room.",
|
||||||
"Joins room with given alias": "Joins room with given alias",
|
"Joins room with given alias": "Joins room with given alias",
|
||||||
"Jump to first unread message.": "Jump to first unread message.",
|
"Jump to first unread message.": "Jump to first unread message.",
|
||||||
|
"Jump to read receipt": "Jump to read receipt",
|
||||||
"%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.",
|
"%(senderName)s kicked %(targetName)s.": "%(senderName)s kicked %(targetName)s.",
|
||||||
"Kick": "Kick",
|
"Kick": "Kick",
|
||||||
"Kicks user with given id": "Kicks user with given id",
|
"Kicks user with given id": "Kicks user with given id",
|
||||||
|
@ -289,6 +290,7 @@
|
||||||
"matrix-react-sdk version:": "matrix-react-sdk version:",
|
"matrix-react-sdk version:": "matrix-react-sdk version:",
|
||||||
"Matrix Apps": "Matrix Apps",
|
"Matrix Apps": "Matrix Apps",
|
||||||
"Members only": "Members only",
|
"Members only": "Members only",
|
||||||
|
"Disable big emoji in chat": "Disable big emoji in chat",
|
||||||
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
"Disable Emoji suggestions while typing": "Disable Emoji suggestions while typing",
|
||||||
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
"Message not sent due to unknown devices being present": "Message not sent due to unknown devices being present",
|
||||||
"Missing room_id in request": "Missing room_id in request",
|
"Missing room_id in request": "Missing room_id in request",
|
||||||
|
@ -609,6 +611,7 @@
|
||||||
"Room": "Room",
|
"Room": "Room",
|
||||||
"Copied!": "Copied!",
|
"Copied!": "Copied!",
|
||||||
"Failed to copy": "Failed to copy",
|
"Failed to copy": "Failed to copy",
|
||||||
|
"There's no one else here! Would you like to <a>invite others</a> or <a>stop warning about the empty room</a>?": "There's no one else here! Would you like to <a>invite others</a> or <a>stop warning about the empty room</a>?",
|
||||||
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
"Connectivity to the server has been lost.": "Connectivity to the server has been lost.",
|
||||||
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
"Sent messages will be stored until your connection has returned.": "Sent messages will be stored until your connection has returned.",
|
||||||
"<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.": "<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
|
"<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.": "<a>Resend all</a> or <a>cancel all</a> now. You can also select individual messages to resend or cancel.",
|
||||||
|
@ -616,6 +619,7 @@
|
||||||
"(~%(count)s results)|other": "(~%(count)s results)",
|
"(~%(count)s results)|other": "(~%(count)s results)",
|
||||||
"Cancel": "Cancel",
|
"Cancel": "Cancel",
|
||||||
"or": "or",
|
"or": "or",
|
||||||
|
"Message Pinning": "Message Pinning",
|
||||||
"Active call": "Active call",
|
"Active call": "Active call",
|
||||||
"Monday": "Monday",
|
"Monday": "Monday",
|
||||||
"Tuesday": "Tuesday",
|
"Tuesday": "Tuesday",
|
||||||
|
@ -632,6 +636,7 @@
|
||||||
"quote": "quote",
|
"quote": "quote",
|
||||||
"bullet": "bullet",
|
"bullet": "bullet",
|
||||||
"numbullet": "numbullet",
|
"numbullet": "numbullet",
|
||||||
|
"Remove avatar": "Remove avatar",
|
||||||
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times",
|
"%(severalUsers)sjoined %(repeats)s times": "%(severalUsers)sjoined %(repeats)s times",
|
||||||
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times",
|
"%(oneUser)sjoined %(repeats)s times": "%(oneUser)sjoined %(repeats)s times",
|
||||||
"%(severalUsers)sjoined": "%(severalUsers)sjoined",
|
"%(severalUsers)sjoined": "%(severalUsers)sjoined",
|
||||||
|
@ -785,7 +790,7 @@
|
||||||
"Start chatting": "Start chatting",
|
"Start chatting": "Start chatting",
|
||||||
"Start Chatting": "Start Chatting",
|
"Start Chatting": "Start Chatting",
|
||||||
"Click on the button below to start chatting!": "Click on the button below to start chatting!",
|
"Click on the button below to start chatting!": "Click on the button below to start chatting!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName changed the room avatar to <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s changed the room avatar to <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
|
||||||
"Username available": "Username available",
|
"Username available": "Username available",
|
||||||
|
@ -885,6 +890,8 @@
|
||||||
"Add rooms to the group summary": "Add rooms to the group summary",
|
"Add rooms to the group summary": "Add rooms to the group summary",
|
||||||
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
|
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
|
||||||
"Room name or alias": "Room name or alias",
|
"Room name or alias": "Room name or alias",
|
||||||
|
"Pinned Messages": "Pinned Messages",
|
||||||
|
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.",
|
||||||
"You are an administrator of this group": "You are an administrator of this group",
|
"You are an administrator of this group": "You are an administrator of this group",
|
||||||
"Failed to add the following rooms to the summary of %(groupId)s:": "Failed to add the following rooms to the summary of %(groupId)s:",
|
"Failed to add the following rooms to the summary of %(groupId)s:": "Failed to add the following rooms to the summary of %(groupId)s:",
|
||||||
"Failed to remove the room from the summary of %(groupId)s": "Failed to remove the room from the summary of %(groupId)s",
|
"Failed to remove the room from the summary of %(groupId)s": "Failed to remove the room from the summary of %(groupId)s",
|
||||||
|
|
|
@ -705,7 +705,7 @@
|
||||||
"Idle": "Idle",
|
"Idle": "Idle",
|
||||||
"Offline": "Offline",
|
"Offline": "Offline",
|
||||||
"Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)",
|
"Disable URL previews for this room (affects only you)": "Disable URL previews for this room (affects only you)",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName changed the room avatar to <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s changed the room avatar to <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removed the room avatar.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s changed the avatar for %(roomName)s",
|
||||||
"Active call (%(roomName)s)": "Active call (%(roomName)s)",
|
"Active call (%(roomName)s)": "Active call (%(roomName)s)",
|
||||||
|
@ -844,6 +844,7 @@
|
||||||
"+example:%(domain)s": "+example:%(domain)s",
|
"+example:%(domain)s": "+example:%(domain)s",
|
||||||
"Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s",
|
"Group IDs must be of the form +localpart:%(domain)s": "Group IDs must be of the form +localpart:%(domain)s",
|
||||||
"Room creation failed": "Room creation failed",
|
"Room creation failed": "Room creation failed",
|
||||||
|
"Pinned Messages": "Pinned Messages",
|
||||||
"You are a member of these groups:": "You are a member of these groups:",
|
"You are a member of these groups:": "You are a member of these groups:",
|
||||||
"Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.",
|
"Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.": "Create a group to represent your community! Define a set of rooms and your own custom homepage to mark out your space in the Matrix universe.",
|
||||||
"Join an existing group": "Join an existing group",
|
"Join an existing group": "Join an existing group",
|
||||||
|
@ -859,6 +860,7 @@
|
||||||
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
"%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s",
|
||||||
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
"Robot check is currently unavailable on desktop - please use a <a>web browser</a>": "Robot check is currently unavailable on desktop - please use a <a>web browser</a>",
|
||||||
"Verifies a user, device, and pubkey tuple": "Verifies a user, device, and pubkey tuple",
|
"Verifies a user, device, and pubkey tuple": "Verifies a user, device, and pubkey tuple",
|
||||||
|
"%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.",
|
||||||
"It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s",
|
"It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s": "It is currently only possible to create groups on your own home server: use a group ID ending with %(domain)s",
|
||||||
"To join an existing group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.": "To join an existing group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.",
|
"To join an existing group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.": "To join an existing group you'll have to know its group identifier; this will look something like <i>+example:matrix.org</i>.",
|
||||||
"%(weekDayName)s, %(monthName)s %(day)s": "%(weekDayName)s, %(monthName)s %(day)s",
|
"%(weekDayName)s, %(monthName)s %(day)s": "%(weekDayName)s, %(monthName)s %(day)s",
|
||||||
|
|
|
@ -736,7 +736,7 @@
|
||||||
"Start chatting": "Hasi txateatzen",
|
"Start chatting": "Hasi txateatzen",
|
||||||
"Start Chatting": "Hasi txateatzen",
|
"Start Chatting": "Hasi txateatzen",
|
||||||
"Click on the button below to start chatting!": "Egin klik beheko botoian txateatzen hasteko!",
|
"Click on the button below to start chatting!": "Egin klik beheko botoian txateatzen hasteko!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName erabiltzaileak gelaren abatarra aldatu du beste honetara: <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s erabiltzaileak gelaren abatarra aldatu du beste honetara: <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s erabiltzaileak gelaren abatarra ezabatu du.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s erabiltzaileak gelaren abatarra ezabatu du.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s erabiltzaileak %(roomName)s gelaren abatarra aldatu du",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s erabiltzaileak %(roomName)s gelaren abatarra aldatu du",
|
||||||
"Username available": "Erabiltzaile-izena eskuragarri dago",
|
"Username available": "Erabiltzaile-izena eskuragarri dago",
|
||||||
|
|
|
@ -637,7 +637,7 @@
|
||||||
"for %(amount)sm": "depuis %(amount)sm",
|
"for %(amount)sm": "depuis %(amount)sm",
|
||||||
"for %(amount)sh": "depuis %(amount)sh",
|
"for %(amount)sh": "depuis %(amount)sh",
|
||||||
"for %(amount)sd": "depuis %(amount)sj",
|
"for %(amount)sd": "depuis %(amount)sj",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName a changé l’avatar du salon en <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s a changé l’avatar du salon en <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s a supprimé l'avatar du salon.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s a supprimé l'avatar du salon.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s a changé l’avatar de %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s a changé l’avatar de %(roomName)s",
|
||||||
"Device already verified!": "Appareil déjà vérifié !",
|
"Device already verified!": "Appareil déjà vérifié !",
|
||||||
|
|
|
@ -723,7 +723,7 @@
|
||||||
"Start chatting": "Csevegés indítása",
|
"Start chatting": "Csevegés indítása",
|
||||||
"Start Chatting": "Csevegés indítása",
|
"Start Chatting": "Csevegés indítása",
|
||||||
"Click on the button below to start chatting!": "Csevegés indításához kattints a gombra alább!",
|
"Click on the button below to start chatting!": "Csevegés indításához kattints a gombra alább!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName megváltoztatta a szoba avatar képét: <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s megváltoztatta a szoba avatar képét: <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s törölte a szoba avatar képét.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s törölte a szoba avatar képét.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s megváltoztatta %(roomName)s szoba avatar képét",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s megváltoztatta %(roomName)s szoba avatar képét",
|
||||||
"Username available": "Szabad felhasználói név",
|
"Username available": "Szabad felhasználói név",
|
||||||
|
|
|
@ -743,7 +743,7 @@
|
||||||
"Start chatting": "이야기하기",
|
"Start chatting": "이야기하기",
|
||||||
"Start Chatting": "이야기하기",
|
"Start Chatting": "이야기하기",
|
||||||
"Click on the button below to start chatting!": "이야기하려면 아래 버튼을 누르세요!",
|
"Click on the button below to start chatting!": "이야기하려면 아래 버튼을 누르세요!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName님이 방 아바타를 <img/>로 바꾸셨어요",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s님이 방 아바타를 <img/>로 바꾸셨어요",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s님이 방 아바타를 지우셨어요.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s님이 방 아바타를 지우셨어요.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s가 %(roomName)s 방의 아바타를 바꾸셨어요",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s가 %(roomName)s 방의 아바타를 바꾸셨어요",
|
||||||
"Username available": "쓸 수 있는 사용자 이름",
|
"Username available": "쓸 수 있는 사용자 이름",
|
||||||
|
|
|
@ -619,7 +619,7 @@
|
||||||
"Dec": "Dec.",
|
"Dec": "Dec.",
|
||||||
"Set a display name:": "Iestatīt redzamo vārdu:",
|
"Set a display name:": "Iestatīt redzamo vārdu:",
|
||||||
"This image cannot be displayed.": "Šo attēlu nav iespējams parādīt.",
|
"This image cannot be displayed.": "Šo attēlu nav iespējams parādīt.",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName nomainīja istabas attēlu uz <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s nomainīja istabas attēlu uz <img/>",
|
||||||
"Upload an avatar:": "Augšuplādē profila attēlu:",
|
"Upload an avatar:": "Augšuplādē profila attēlu:",
|
||||||
"This server does not support authentication with a phone number.": "Šis serveris neatbalsta autentifikāciju pēc telefona numura.",
|
"This server does not support authentication with a phone number.": "Šis serveris neatbalsta autentifikāciju pēc telefona numura.",
|
||||||
"Missing password.": "Trūkst parole.",
|
"Missing password.": "Trūkst parole.",
|
||||||
|
|
|
@ -746,7 +746,7 @@
|
||||||
"Start chatting": "Start met praten",
|
"Start chatting": "Start met praten",
|
||||||
"Start Chatting": "Start Met Praten",
|
"Start Chatting": "Start Met Praten",
|
||||||
"Click on the button below to start chatting!": "Klik op de knop hieronder om te starten met praten!",
|
"Click on the button below to start chatting!": "Klik op de knop hieronder om te starten met praten!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName heeft de ruimte avatar aangepast naar <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s heeft de ruimte avatar aangepast naar <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s heeft de ruimte avatar verwijderd.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s heeft de ruimte avatar verwijderd.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s veranderde de avatar voor %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s veranderde de avatar voor %(roomName)s",
|
||||||
"Username available": "Gebruikersnaam beschikbaar",
|
"Username available": "Gebruikersnaam beschikbaar",
|
||||||
|
|
|
@ -771,7 +771,7 @@
|
||||||
"for %(amount)sd": "%(amount)s dni",
|
"for %(amount)sd": "%(amount)s dni",
|
||||||
"Idle": "Bezczynny",
|
"Idle": "Bezczynny",
|
||||||
"Check for update": "Sprawdź aktualizacje",
|
"Check for update": "Sprawdź aktualizacje",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName zmienił awatar pokoju na <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s zmienił awatar pokoju na <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s usunął awatar pokoju.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s usunął awatar pokoju.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s zmienił awatar %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s zmienił awatar %(roomName)s",
|
||||||
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "To będzie twoja nazwa konta na <span></span> serwerze domowym; możesz też wybrać <a>inny serwer</a>.",
|
"This will be your account name on the <span></span> homeserver, or you can pick a <a>different server</a>.": "To będzie twoja nazwa konta na <span></span> serwerze domowym; możesz też wybrać <a>inny serwer</a>.",
|
||||||
|
|
|
@ -699,7 +699,7 @@
|
||||||
"for %(amount)sd": "por %(amount)sd",
|
"for %(amount)sd": "por %(amount)sd",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName alterou a imagem da sala para <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s alterou a imagem da sala para <img/>",
|
||||||
"Missing Media Permissions, click here to request.": "Faltam permissões para uso de mídia no seu computador. Clique aqui para solicitá-las.",
|
"Missing Media Permissions, click here to request.": "Faltam permissões para uso de mídia no seu computador. Clique aqui para solicitá-las.",
|
||||||
"No Microphones detected": "Não foi detetado nenhum microfone",
|
"No Microphones detected": "Não foi detetado nenhum microfone",
|
||||||
"No Webcams detected": "Não foi detetada nenhuma Webcam",
|
"No Webcams detected": "Não foi detetada nenhuma Webcam",
|
||||||
|
|
|
@ -696,7 +696,7 @@
|
||||||
"for %(amount)sd": "por %(amount)sd",
|
"for %(amount)sd": "por %(amount)sd",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s removeu a imagem da sala.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s alterou a imagem da sala %(roomName)s",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName alterou a imagem da sala para <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s alterou a imagem da sala para <img/>",
|
||||||
"Missing Media Permissions, click here to request.": "Faltam permissões para uso de mídia no seu computador. Clique aqui para solicitá-las.",
|
"Missing Media Permissions, click here to request.": "Faltam permissões para uso de mídia no seu computador. Clique aqui para solicitá-las.",
|
||||||
"No Microphones detected": "Não foi detectado nenhum microfone",
|
"No Microphones detected": "Não foi detectado nenhum microfone",
|
||||||
"No Webcams detected": "Não foi detectada nenhuma Webcam",
|
"No Webcams detected": "Não foi detectada nenhuma Webcam",
|
||||||
|
|
|
@ -705,7 +705,7 @@
|
||||||
"Idle": "Неактивен",
|
"Idle": "Неактивен",
|
||||||
"Offline": "Не в сети",
|
"Offline": "Не в сети",
|
||||||
"Disable URL previews for this room (affects only you)": "Отключить предпросмотр URL-адресов для этой комнаты (влияет только на вас)",
|
"Disable URL previews for this room (affects only you)": "Отключить предпросмотр URL-адресов для этой комнаты (влияет только на вас)",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName сменил аватар комнаты на <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s сменил аватар комнаты на <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s удалил аватар комнаты.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s сменил аватар для %(roomName)s",
|
||||||
"Create new room": "Создать новую комнату",
|
"Create new room": "Создать новую комнату",
|
||||||
|
|
|
@ -738,7 +738,7 @@
|
||||||
"Start chatting": "Sohbeti başlat",
|
"Start chatting": "Sohbeti başlat",
|
||||||
"Start Chatting": "Sohbeti Başlat",
|
"Start Chatting": "Sohbeti Başlat",
|
||||||
"Click on the button below to start chatting!": "Sohbeti başlatmak için aşağıdaki butona tıklayın!",
|
"Click on the button below to start chatting!": "Sohbeti başlatmak için aşağıdaki butona tıklayın!",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName odanın avatarını <img/> olarak çevirdi",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s odanın avatarını <img/> olarak çevirdi",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s odanın avatarını kaldırdı.",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s odanın avatarını kaldırdı.",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s %(roomName)s için avatarı değiştirdi",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s %(roomName)s için avatarı değiştirdi",
|
||||||
"Username available": "Kullanıcı ismi uygun",
|
"Username available": "Kullanıcı ismi uygun",
|
||||||
|
|
|
@ -228,7 +228,7 @@
|
||||||
"Idle": "閒置",
|
"Idle": "閒置",
|
||||||
"Offline": "下線",
|
"Offline": "下線",
|
||||||
"Disable URL previews for this room (affects only you)": "在這個房間禁止URL預覽(只影響你)",
|
"Disable URL previews for this room (affects only you)": "在這個房間禁止URL預覽(只影響你)",
|
||||||
"$senderDisplayName changed the room avatar to <img/>": "$senderDisplayName 更改了聊天室的圖像為 <img/>",
|
"%(senderDisplayName)s changed the room avatar to <img/>": "%(senderDisplayName)s 更改了聊天室的圖像為 <img/>",
|
||||||
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s 移除了聊天室圖片。",
|
"%(senderDisplayName)s removed the room avatar.": "%(senderDisplayName)s 移除了聊天室圖片。",
|
||||||
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s 更改了聊天室 %(roomName)s 圖像",
|
"%(senderDisplayName)s changed the avatar for %(roomName)s": "%(senderDisplayName)s 更改了聊天室 %(roomName)s 圖像",
|
||||||
"Cancel": "取消",
|
"Cancel": "取消",
|
||||||
|
|
|
@ -109,8 +109,9 @@ export function _tJsx(jsxText, patterns, subs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution)
|
// The translation returns text so there's no XSS vector here (no unsafe HTML, no code execution)
|
||||||
const tJsxText = _t(jsxText);
|
const tJsxText = _t(jsxText, {interpolate: false});
|
||||||
const output = [tJsxText];
|
const output = [tJsxText];
|
||||||
|
|
||||||
for (let i = 0; i < patterns.length; i++) {
|
for (let i = 0; i < patterns.length; i++) {
|
||||||
// convert the last element in 'output' into 3 elements (pre-text, sub function, post-text).
|
// convert the last element in 'output' into 3 elements (pre-text, sub function, post-text).
|
||||||
// Rinse and repeat for other patterns (using post-text).
|
// Rinse and repeat for other patterns (using post-text).
|
||||||
|
|
Loading…
Reference in a new issue