Merge branches 'develop' and 't3chguy/rightpanel' of github.com:matrix-org/matrix-react-sdk into t3chguy/rightpanel

pull/21833/head
Michael Telatynski 2020-01-15 00:03:18 +00:00
commit 5a5a0d2233
31 changed files with 1333 additions and 102 deletions

View File

@ -1,3 +1,193 @@
Changes in [1.7.6](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6) (2020-01-13)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6-rc.2...v1.7.6)
* Repair community member info panel
[\#3834](https://github.com/matrix-org/matrix-react-sdk/pull/3834)
* Add feature flag around the presence indicator in room list
[\#3833](https://github.com/matrix-org/matrix-react-sdk/pull/3833)
Changes in [1.7.6-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.2) (2020-01-08)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.6-rc.1...v1.7.6-rc.2)
* Strip all variation selectors on emoji
[\#3818](https://github.com/matrix-org/matrix-react-sdk/pull/3818)
Changes in [1.7.6-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.6-rc.1) (2020-01-06)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5...v1.7.6-rc.1)
* Deduplicate recent emoji
[\#3806](https://github.com/matrix-org/matrix-react-sdk/pull/3806)
* Fix ability to remove avatars
[\#3803](https://github.com/matrix-org/matrix-react-sdk/pull/3803)
* Update from Weblate
[\#3810](https://github.com/matrix-org/matrix-react-sdk/pull/3810)
* User Info fetch latest RoomMember instead of showing historical data
[\#3788](https://github.com/matrix-org/matrix-react-sdk/pull/3788)
* Remove all usages of slate in favour of CIDER
[\#3808](https://github.com/matrix-org/matrix-react-sdk/pull/3808)
* Use display name when pinned messages are changed
[\#3809](https://github.com/matrix-org/matrix-react-sdk/pull/3809)
* Fix inverted diff line highlighting in dark theme
[\#3790](https://github.com/matrix-org/matrix-react-sdk/pull/3790)
* Bridge info settings tab
[\#3693](https://github.com/matrix-org/matrix-react-sdk/pull/3693)
* Send the labs flags the client is running with in rageshake
[\#3805](https://github.com/matrix-org/matrix-react-sdk/pull/3805)
* Initial implementation of FTUE user lists design
[\#3792](https://github.com/matrix-org/matrix-react-sdk/pull/3792)
* Update key backup creation and recovery paths for SSSS
[\#3800](https://github.com/matrix-org/matrix-react-sdk/pull/3800)
* Don't fail if logs exists and is an empty dir
[\#3798](https://github.com/matrix-org/matrix-react-sdk/pull/3798)
* Comment remaining non-cross-signing-compliant components
[\#3799](https://github.com/matrix-org/matrix-react-sdk/pull/3799)
* Remove 'unverify' from UserInfoPanel
[\#3797](https://github.com/matrix-org/matrix-react-sdk/pull/3797)
* Use deviceTrust when displaying key backup trust status
[\#3795](https://github.com/matrix-org/matrix-react-sdk/pull/3795)
* Don't crash if a keyshare request is removed
[\#3793](https://github.com/matrix-org/matrix-react-sdk/pull/3793)
* Convert /verify to checkDeviceTrust
[\#3794](https://github.com/matrix-org/matrix-react-sdk/pull/3794)
* Remove E2eIcon onClick
[\#3791](https://github.com/matrix-org/matrix-react-sdk/pull/3791)
* support channel names with slash in name/alias
[\#3778](https://github.com/matrix-org/matrix-react-sdk/pull/3778)
* Fix NPE when filtering the room list
[\#3787](https://github.com/matrix-org/matrix-react-sdk/pull/3787)
* Turn RoomAliasField into properly controlled and use in RoomSettings
[\#3782](https://github.com/matrix-org/matrix-react-sdk/pull/3782)
* fuzzy-sort MemberList
[\#3783](https://github.com/matrix-org/matrix-react-sdk/pull/3783)
* Serialize file uploads into room to match confirmation dialog order
[\#3786](https://github.com/matrix-org/matrix-react-sdk/pull/3786)
* Do not show Top Unread Messages Bar and Jump to bottom button if searching
[\#3785](https://github.com/matrix-org/matrix-react-sdk/pull/3785)
* Fix sticker picker chevron offset calculation
[\#3784](https://github.com/matrix-org/matrix-react-sdk/pull/3784)
* Fix not being able to promote others to the same power level as your own
[\#3781](https://github.com/matrix-org/matrix-react-sdk/pull/3781)
* Room Tile DMs online/active green dot
[\#3751](https://github.com/matrix-org/matrix-react-sdk/pull/3751)
* Fix spelling and grammar in README
[\#3780](https://github.com/matrix-org/matrix-react-sdk/pull/3780)
* Reintroduce working resizer code for right panel
[\#3776](https://github.com/matrix-org/matrix-react-sdk/pull/3776)
* Fix wrong scope binding on openHelp for TopLeftMenu
[\#3775](https://github.com/matrix-org/matrix-react-sdk/pull/3775)
* UserInfo hide kick/mute buttons if they make no sense
[\#3774](https://github.com/matrix-org/matrix-react-sdk/pull/3774)
* Fix duplicate Incoming Call prompt on Community Invite sublist
[\#3773](https://github.com/matrix-org/matrix-react-sdk/pull/3773)
* Apply new design to highlighted tags and add toggle mechanic
[\#3755](https://github.com/matrix-org/matrix-react-sdk/pull/3755)
* stop using ReactDOM.findDOMNode in componentWillUnmount, use refs
[\#3771](https://github.com/matrix-org/matrix-react-sdk/pull/3771)
* Add alt="" to presentational images
[\#3772](https://github.com/matrix-org/matrix-react-sdk/pull/3772)
* Fix room list filtering weird case sensitivity
[\#3759](https://github.com/matrix-org/matrix-react-sdk/pull/3759)
* Don't show the 'verify' button if the user is verified
[\#3758](https://github.com/matrix-org/matrix-react-sdk/pull/3758)
* Switch to using checkDeviceTrust
[\#3757](https://github.com/matrix-org/matrix-react-sdk/pull/3757)
* Migrate away from React Legacy contexts API
[\#3743](https://github.com/matrix-org/matrix-react-sdk/pull/3743)
* Migrate key backups to SSSS
[\#3749](https://github.com/matrix-org/matrix-react-sdk/pull/3749)
* Get rid of stripped-emoji.json in favour of an in-memory single source of
truth
[\#3745](https://github.com/matrix-org/matrix-react-sdk/pull/3745)
* Combine cross signing and verification over DM feature flags
[\#3753](https://github.com/matrix-org/matrix-react-sdk/pull/3753)
* apply unhomoglyph when filtering room list to fuzzify it
[\#3754](https://github.com/matrix-org/matrix-react-sdk/pull/3754)
* Make EmojiPicker an unmanaged Context Menu as it is too complex to be
managed
[\#3746](https://github.com/matrix-org/matrix-react-sdk/pull/3746)
* Internationalise M_TOO_LARGE error from Synapse
[\#3750](https://github.com/matrix-org/matrix-react-sdk/pull/3750)
* Replace UserInfo avatar with <MemberAvatar/> for fallback logic
[\#3748](https://github.com/matrix-org/matrix-react-sdk/pull/3748)
* Dropdown stop keyboard propagation if key handled
[\#3741](https://github.com/matrix-org/matrix-react-sdk/pull/3741)
* Fix right panel for multiple member info viewings
[\#3742](https://github.com/matrix-org/matrix-react-sdk/pull/3742)
* Fix Field validation tooltip sticking if blurred before async validation
resolved
[\#3740](https://github.com/matrix-org/matrix-react-sdk/pull/3740)
* Fix UserInfo exploding without a room being passed to it
[\#3738](https://github.com/matrix-org/matrix-react-sdk/pull/3738)
* Fix room directory maintaining and error state
[\#3737](https://github.com/matrix-org/matrix-react-sdk/pull/3737)
* Stop trapping tab in AddressPickerDialog
[\#3735](https://github.com/matrix-org/matrix-react-sdk/pull/3735)
* Stop using KeyboardEvent.keyCode as it is deprecated
[\#3736](https://github.com/matrix-org/matrix-react-sdk/pull/3736)
* Implement new design for uploading/removing avatars
[\#3733](https://github.com/matrix-org/matrix-react-sdk/pull/3733)
* Fix aspect ratio on room/profile avatar preview
[\#3731](https://github.com/matrix-org/matrix-react-sdk/pull/3731)
* Switch to react-focus-lock for it to comprehend Portals
[\#3732](https://github.com/matrix-org/matrix-react-sdk/pull/3732)
* Make combobox dropdown keyboard and screen reader accessible
[\#3729](https://github.com/matrix-org/matrix-react-sdk/pull/3729)
* Verify users when cross-signing enabled
[\#3728](https://github.com/matrix-org/matrix-react-sdk/pull/3728)
* Update from Weblate
[\#3730](https://github.com/matrix-org/matrix-react-sdk/pull/3730)
* Improve a11y of the unignore button in Settings
[\#3727](https://github.com/matrix-org/matrix-react-sdk/pull/3727)
* Fix ToggleSwitch A11Y (trapping tab and switch v. checkbox)
[\#3726](https://github.com/matrix-org/matrix-react-sdk/pull/3726)
* Make URL previews dismissable via keyboard and accessible to screen readers
[\#3725](https://github.com/matrix-org/matrix-react-sdk/pull/3725)
* Create new key backups using secret storage
[\#3720](https://github.com/matrix-org/matrix-react-sdk/pull/3720)
* Replace sign-ins with sessions
[\#3721](https://github.com/matrix-org/matrix-react-sdk/pull/3721)
* Refactor RightPanel to match expected behaviour
[\#3703](https://github.com/matrix-org/matrix-react-sdk/pull/3703)
* Render policy room event updates in the timeline
[\#3716](https://github.com/matrix-org/matrix-react-sdk/pull/3716)
* Wrap the await call for unknown device lookups
[\#3718](https://github.com/matrix-org/matrix-react-sdk/pull/3718)
* Add testing flow to bootstrap secret storage
[\#3640](https://github.com/matrix-org/matrix-react-sdk/pull/3640)
* Fix remaining context menu regressions
[\#3715](https://github.com/matrix-org/matrix-react-sdk/pull/3715)
* Migrate away from React Legacy string refs
[\#3712](https://github.com/matrix-org/matrix-react-sdk/pull/3712)
* Update copy for DM invites
[\#3706](https://github.com/matrix-org/matrix-react-sdk/pull/3706)
* Fix message action bar reaction picker regression
[\#3714](https://github.com/matrix-org/matrix-react-sdk/pull/3714)
* Add what-input to allow different scoping to focus-visible for MAB a11y
[\#3709](https://github.com/matrix-org/matrix-react-sdk/pull/3709)
* Mark the This/All Rooms scope buttons as radios for a11y
[\#3708](https://github.com/matrix-org/matrix-react-sdk/pull/3708)
* Switch ReactionsRowButton to an AccessibleButton for space/enter handling
[\#3707](https://github.com/matrix-org/matrix-react-sdk/pull/3707)
* Change the (edited) link to an AccessibleButton for a11y
[\#3710](https://github.com/matrix-org/matrix-react-sdk/pull/3710)
* Update from Weblate
[\#3713](https://github.com/matrix-org/matrix-react-sdk/pull/3713)
* Fix ?via= args in SpecPermalinkConstructor.js
[\#3694](https://github.com/matrix-org/matrix-react-sdk/pull/3694)
* Don't mark a room as unread when server ACLs are set
[\#3705](https://github.com/matrix-org/matrix-react-sdk/pull/3705)
* Make reaction buttons more accessible
[\#3704](https://github.com/matrix-org/matrix-react-sdk/pull/3704)
* yarn upgrade
[\#3701](https://github.com/matrix-org/matrix-react-sdk/pull/3701)
* Make CI scripts executable
[\#3698](https://github.com/matrix-org/matrix-react-sdk/pull/3698)
* ARIA compliant context menus
[\#3611](https://github.com/matrix-org/matrix-react-sdk/pull/3611)
Changes in [1.7.5](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v1.7.5) (2019-12-09)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v1.7.5-rc.1...v1.7.5)

View File

@ -1,6 +1,6 @@
{
"name": "matrix-react-sdk",
"version": "1.7.5",
"version": "1.7.6",
"description": "SDK for matrix.org using React",
"author": "matrix.org",
"repository": {
@ -88,7 +88,7 @@
"linkifyjs": "^2.1.6",
"lodash": "^4.17.14",
"lolex": "4.2",
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop",
"matrix-js-sdk": "3.0.0",
"optimist": "^0.6.1",
"pako": "^1.0.5",
"png-chunks-extract": "^1.0.0",

View File

@ -21,15 +21,51 @@ limitations under the License.
.mx_DMInviteDialog_editor {
flex: 1;
width: 100%; // Needed to make the Field inside grow
}
background-color: $user-tile-hover-bg-color;
border-radius: 4px;
min-height: 25px;
padding-left: 8px;
overflow-x: hidden;
overflow-y: auto;
.mx_Field {
margin: 0;
.mx_DMInviteDialog_userTile {
display: inline-block;
float: left;
position: relative;
top: 7px;
}
// Using a textarea for this element, to circumvent autofill
// Mostly copied from AddressPickerDialog
textarea,
textarea:focus {
height: 34px;
line-height: 34px;
font-size: 14px;
padding-left: 12px;
margin: 0 !important;
border: 0 !important;
outline: 0 !important;
resize: none;
overflow: hidden;
box-sizing: border-box;
word-wrap: nowrap;
// Roughly fill about 2/5ths of the available space. This is to try and 'fill' the
// remaining space after a bunch of pills, but is a bit hacky. Ideally we'd have
// support for "fill remaining width", but traditional tricks don't work with what
// we're pushing into this "field". Flexbox just makes things worse. The theory is
// that users won't need more than about 2/5ths of the input to find the person
// they're looking for.
width: 40%;
}
}
.mx_DMInviteDialog_goButton {
width: 48px;
margin-left: 10px;
height: 25px;
line-height: 25px;
}
}
@ -57,6 +93,43 @@ limitations under the License.
vertical-align: middle;
}
.mx_DMInviteDialog_roomTile_avatarStack {
display: inline-block;
position: relative;
width: 36px;
height: 36px;
& > * {
position: absolute;
top: 0;
left: 0;
}
}
.mx_DMInviteDialog_roomTile_selected {
width: 36px;
height: 36px;
border-radius: 36px;
background-color: $username-variant1-color;
display: inline-block;
position: relative;
&::before {
content: "";
width: 24px;
height: 24px;
grid-column: 1;
grid-row: 1;
mask-image: url('$(res)/img/feather-customised/check.svg');
mask-size: 100%;
mask-repeat: no-repeat;
position: absolute;
top: 6px; // 50%
left: 6px; // 50%
background-color: #ffffff; // this is fine without a var because it's for both themes
}
}
.mx_DMInviteDialog_roomTile_name {
font-weight: 600;
font-size: 14px;
@ -83,3 +156,42 @@ limitations under the License.
}
}
// Many of these styles are stolen from mx_UserPill, but adjusted for the invite dialog.
.mx_DMInviteDialog_userTile {
margin-right: 8px;
.mx_DMInviteDialog_userTile_pill {
background-color: $username-variant1-color;
border-radius: 12px;
display: inline-block;
height: 24px;
line-height: 24px;
padding-left: 8px;
padding-right: 8px;
color: #ffffff; // this is fine without a var because it's for both themes
.mx_DMInviteDialog_userTile_avatar {
border-radius: 20px;
position: relative;
left: -5px;
top: 2px;
}
img.mx_DMInviteDialog_userTile_avatar {
vertical-align: top;
}
.mx_DMInviteDialog_userTile_name {
vertical-align: top;
}
.mx_DMInviteDialog_userTile_threepidAvatar {
background-color: #ffffff; // this is fine without a var because it's for both themes
}
}
.mx_DMInviteDialog_userTile_remove {
display: inline-block;
margin-left: 4px;
}
}

View File

@ -263,3 +263,24 @@ limitations under the License.
.mx_RoomHeader_pinsIndicatorUnread {
background-color: $pinned-unread-color;
}
.mx_RoomHeader_PrivateIcon.mx_RoomHeader_isPrivate {
width: 12px;
height: 12px;
position: relative;
display: block !important;
&::before {
background-color: $roomtile-name-color;
mask-image: url('$(res)/img/feather-customised/lock-solid.svg');
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
}

View File

@ -200,3 +200,31 @@ limitations under the License.
.mx_GroupInviteTile .mx_RoomTile_name {
flex: 1;
}
.mx_RoomTile.mx_RoomTile.mx_RoomTile_isPrivate .mx_RoomTile_name {
// Scoot the padding in a bit from 6px to make it look better
padding-left: 3px;
}
.mx_RoomTile.mx_RoomTile_isPrivate .mx_RoomTile_PrivateIcon {
width: 12px;
height: 12px;
position: relative;
display: block !important;
// Align the padlock with unencrypted room names
margin-left: 6px;
&::before {
background-color: $roomtile-name-color;
mask-image: url('$(res)/img/feather-customised/lock-solid.svg');
mask-position: center;
mask-repeat: no-repeat;
mask-size: contain;
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
}

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 13C3 11.8954 3.89543 11 5 11H19C20.1046 11 21 11.8954 21 13V20C21 21.1046 20.1046 22 19 22H5C3.89543 22 3 21.1046 3 20V13Z" fill="#2E2F32" stroke="#2E2F32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M7 11V7C7 4.23858 9.23858 2 12 2C14.7614 2 17 4.23858 17 7V11" stroke="#2E2F32" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 532 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="65.631" height="67.981"><defs><filter x="-.059" y="-.079" width="1.118" height="1.158" filterUnits="objectBoundingBox" id="a"><feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0.473684211 0 0 0 0 1 0 0 0 0.241258741 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" transform="matrix(3.40907 0 0 3.40907 -1493.716 -795.144)" fill="none" fill-rule="evenodd" stroke="#368bd6" stroke-linecap="round" stroke-linejoin="round"><g transform="translate(441.5 237.5)"><circle r="2.286" cy="5.714" cx="6.286"/><path d="M8.571 3.429v2.857a1.714 1.714 0 103.429 0v-.572a5.714 5.714 0 10-2.24 4.537"/></g></g></svg>

After

Width:  |  Height:  |  Size: 918 B

View File

@ -0,0 +1 @@
<svg width="58" height="60" viewBox="26 25 6 6" xmlns="http://www.w3.org/2000/svg"><defs><filter x="-5.9%" y="-7.9%" width="111.8%" height="115.8%" filterUnits="objectBoundingBox" id="a"><feOffset dy="2" in="SourceAlpha" result="shadowOffsetOuter1"/><feGaussianBlur stdDeviation="16" in="shadowOffsetOuter1" result="shadowBlurOuter1"/><feColorMatrix values="0 0 0 0 0 0 0 0 0 0.473684211 0 0 0 0 1 0 0 0 0.241258741 0" in="shadowBlurOuter1" result="shadowMatrixOuter1"/><feMerge><feMergeNode in="shadowMatrixOuter1"/><feMergeNode in="SourceGraphic"/></feMerge></filter></defs><g filter="url(#a)" transform="translate(-406 -215)" stroke="#61708B"><path d="M438 240l-6 6M432 240l6 6"/></g></svg>

After

Width:  |  Height:  |  Size: 693 B

View File

@ -21,14 +21,17 @@ import DMRoomMap from './utils/DMRoomMap';
module.exports = {
avatarUrlForMember: function(member, width, height, resizeMethod) {
let url = member.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
Math.floor(width * window.devicePixelRatio),
Math.floor(height * window.devicePixelRatio),
resizeMethod,
false,
false,
);
let url;
if (member && member.getAvatarUrl) {
url = member.getAvatarUrl(
MatrixClientPeg.get().getHomeserverUrl(),
Math.floor(width * window.devicePixelRatio),
Math.floor(height * window.devicePixelRatio),
resizeMethod,
false,
false,
);
}
if (!url) {
// member can be null here currently since on invites, the JS SDK
// does not have enough info to build a RoomMember object for

View File

@ -24,14 +24,15 @@ import { _t } from '../../../../languageHandler';
import Modal from '../../../../Modal';
const PHASE_LOADING = 0;
const PHASE_MIGRATE = 1;
const PHASE_PASSPHRASE = 2;
const PHASE_PASSPHRASE_CONFIRM = 3;
const PHASE_SHOWKEY = 4;
const PHASE_KEEPITSAFE = 5;
const PHASE_STORING = 6;
const PHASE_DONE = 7;
const PHASE_OPTOUT_CONFIRM = 8;
const PHASE_RESTORE_KEY_BACKUP = 1;
const PHASE_MIGRATE = 2;
const PHASE_PASSPHRASE = 3;
const PHASE_PASSPHRASE_CONFIRM = 4;
const PHASE_SHOWKEY = 5;
const PHASE_KEEPITSAFE = 6;
const PHASE_STORING = 7;
const PHASE_DONE = 8;
const PHASE_OPTOUT_CONFIRM = 9;
const PASSWORD_MIN_SCORE = 4; // So secure, many characters, much complex, wow, etc, etc.
const PASSPHRASE_FEEDBACK_DELAY = 500; // How long after keystroke to offer passphrase feedback, ms.
@ -67,6 +68,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
downloaded: false,
zxcvbnResult: null,
setPassPhrase: false,
backupInfo: null,
backupSigStatus: null,
};
this._fetchBackupInfo();
@ -80,10 +83,16 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
async _fetchBackupInfo() {
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
const backupSigStatus = await MatrixClientPeg.get().isKeyBackupTrusted(backupInfo);
const phase = backupInfo ?
(backupSigStatus.usable ? PHASE_MIGRATE : PHASE_RESTORE_KEY_BACKUP) :
PHASE_PASSPHRASE;
this.setState({
phase: backupInfo ? PHASE_MIGRATE: PHASE_PASSPHRASE,
phase,
backupInfo,
backupSigStatus,
});
}
@ -161,6 +170,14 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
this.props.onFinished(true);
}
_onRestoreKeyBackupClick = () => {
const RestoreKeyBackupDialog = sdk.getComponent('dialogs.keybackup.RestoreKeyBackupDialog');
Modal.createTrackedDialog(
'Restore Backup', '', RestoreKeyBackupDialog, null, null,
/* priority = */ false, /* static = */ true,
);
}
_onOptOutClick = () => {
this.setState({phase: PHASE_OPTOUT_CONFIRM});
}
@ -268,6 +285,23 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
return this.state.zxcvbnResult && this.state.zxcvbnResult.score >= PASSWORD_MIN_SCORE;
}
_renderPhaseRestoreKeyBackup() {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t(
"Key Backup is enabled on your account but has not been set " +
"up from this session. To set up secret storage, " +
"restore your key backup.",
)}</p>
<DialogButtons primaryButton={_t('Restore')}
onPrimaryButtonClick={this._onRestoreKeyBackupClick}
onCancel={this._onCancel}
hasCancel={true}
>
</DialogButtons>
</div>;
}
_renderPhaseMigrate() {
// TODO: This is a temporary screen so people who have the labs flag turned on and
// click the button are aware they're making a change to their account.
@ -277,7 +311,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t(
"Secret Storage will be set up using your existing key backup details." +
"Secret Storage will be set up using your existing key backup details. " +
"Your secret storage passphrase and recovery key will be the same as " +
" they were for your key backup",
)}</p>
@ -527,6 +561,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
_titleForPhase(phase) {
switch (phase) {
case PHASE_RESTORE_KEY_BACKUP:
return _t('Restore your Key Backup');
case PHASE_MIGRATE:
return _t('Migrate from Key Backup');
case PHASE_PASSPHRASE:
@ -569,6 +605,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
case PHASE_LOADING:
content = this._renderBusyPhase();
break;
case PHASE_RESTORE_KEY_BACKUP:
content = this._renderPhaseRestoreKeyBackup();
break;
case PHASE_MIGRATE:
content = this._renderPhaseMigrate();
break;

View File

@ -1505,6 +1505,15 @@ export default createReactClass({
"blacklistUnverifiedDevices",
);
cli.setGlobalBlacklistUnverifiedDevices(blacklistEnabled);
// With cross-signing enabled, we send to unknown devices
// without prompting. Any bad-device status the user should
// be aware of will be signalled through the room shield
// changing colour. More advanced behaviour will come once
// we implement more settings.
cli.setGlobalErrorOnUnknownDevices(
!SettingsStore.isFeatureEnabled("feature_cross_signing"),
);
}
},

View File

@ -485,6 +485,7 @@ module.exports = createReactClass({
MatrixClientPeg.get().removeListener("Room.timeline", this.onRoomTimeline);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
MatrixClientPeg.get().removeListener("Room.accountData", this.onRoomAccountData);
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership);
MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);

View File

@ -55,7 +55,7 @@ module.exports = createReactClass({
},
_getState: function(props) {
if (props.member) {
if (props.member && props.member.name) {
return {
name: props.member.name,
title: props.title || props.member.userId,

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import {_t} from "../../../languageHandler";
import sdk from "../../../index";
@ -25,24 +25,56 @@ import {RoomMember} from "matrix-js-sdk/lib/matrix";
import * as humanize from "humanize";
import SdkConfig from "../../../SdkConfig";
import {getHttpUriForMxc} from "matrix-js-sdk/lib/content-repo";
import * as Email from "../../../email";
import {getDefaultIdentityServerUrl, useDefaultIdentityServer} from "../../../utils/IdentityServerUtils";
import {abbreviateUrl} from "../../../utils/UrlUtils";
import dis from "../../../dispatcher";
import IdentityAuthClient from "../../../IdentityAuthClient";
import Modal from "../../../Modal";
// TODO: [TravisR] Make this generic for all kinds of invites
const INITIAL_ROOMS_SHOWN = 3; // Number of rooms to show at first
const INCREMENT_ROOMS_SHOWN = 5; // Number of rooms to add when 'show more' is clicked
class DirectoryMember {
// This is the interface that is expected by various components in this file. It is a bit
// awkward because it also matches the RoomMember class from the js-sdk with some extra support
// for 3PIDs/email addresses.
//
// XXX: We should use TypeScript interfaces instead of this weird "abstract" class.
class Member {
/**
* The display name of this Member. For users this should be their profile's display
* name or user ID if none set. For 3PIDs this should be the 3PID address (email).
*/
get name(): string { throw new Error("Member class not implemented"); }
/**
* The ID of this Member. For users this should be their user ID. For 3PIDs this should
* be the 3PID address (email).
*/
get userId(): string { throw new Error("Member class not implemented"); }
/**
* Gets the MXC URL of this Member's avatar. For users this should be their profile's
* avatar MXC URL or null if none set. For 3PIDs this should always be null.
*/
getMxcAvatarUrl(): string { throw new Error("Member class not implemented"); }
}
class DirectoryMember extends Member {
_userId: string;
_displayName: string;
_avatarUrl: string;
constructor(userDirResult: {user_id: string, display_name: string, avatar_url: string}) {
super();
this._userId = userDirResult.user_id;
this._displayName = userDirResult.display_name;
this._avatarUrl = userDirResult.avatar_url;
}
// These next members are to implement the contract expected by DMRoomTile
// These next class members are for the Member interface
get name(): string {
return this._displayName || this._userId;
}
@ -56,13 +88,93 @@ class DirectoryMember {
}
}
class ThreepidMember extends Member {
_id: string;
constructor(id: string) {
super();
this._id = id;
}
// This is a getter that would be falsey on all other implementations. Until we have
// better type support in the react-sdk we can use this trick to determine the kind
// of 3PID we're dealing with, if any.
get isEmail(): boolean {
return this._id.includes('@');
}
// These next class members are for the Member interface
get name(): string {
return this._id;
}
get userId(): string {
return this._id;
}
getMxcAvatarUrl(): string {
return null;
}
}
class DMUserTile extends React.PureComponent {
static propTypes = {
member: PropTypes.object.isRequired, // Should be a Member (see interface above)
onRemove: PropTypes.func.isRequired, // takes 1 argument, the member being removed
};
_onRemove = (e) => {
// Stop the browser from highlighting text
e.preventDefault();
e.stopPropagation();
this.props.onRemove(this.props.member);
};
render() {
const BaseAvatar = sdk.getComponent("views.avatars.BaseAvatar");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const avatarSize = 20;
const avatar = this.props.member.isEmail
? <img
className='mx_DMInviteDialog_userTile_avatar mx_DMInviteDialog_userTile_threepidAvatar'
src={require("../../../../res/img/icon-email-pill-avatar.svg")}
width={avatarSize} height={avatarSize} />
: <BaseAvatar
className='mx_DMInviteDialog_userTile_avatar'
url={getHttpUriForMxc(
MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(),
avatarSize, avatarSize, "crop")}
name={this.props.member.name}
idName={this.props.member.userId}
width={avatarSize}
height={avatarSize} />;
return (
<span className='mx_DMInviteDialog_userTile'>
<span className='mx_DMInviteDialog_userTile_pill'>
{avatar}
<span className='mx_DMInviteDialog_userTile_name'>{this.props.member.name}</span>
</span>
<AccessibleButton
className='mx_DMInviteDialog_userTile_remove'
onClick={this._onRemove}
>
<img src={require("../../../../res/img/icon-pill-remove.svg")} alt={_t('Remove')} width={8} height={8} />
</AccessibleButton>
</span>
);
}
}
class DMRoomTile extends React.PureComponent {
static propTypes = {
// Has properties to match RoomMember: userId (str), name (str), getMxcAvatarUrl(): string
member: PropTypes.object.isRequired,
member: PropTypes.object.isRequired, // Should be a Member (see interface above)
lastActiveTs: PropTypes.number,
onToggle: PropTypes.func.isRequired,
onToggle: PropTypes.func.isRequired, // takes 1 argument, the member being toggled
highlightWord: PropTypes.string,
isSelected: PropTypes.bool,
};
_onClick = (e) => {
@ -70,7 +182,7 @@ class DMRoomTile extends React.PureComponent {
e.preventDefault();
e.stopPropagation();
this.props.onToggle(this.props.member.userId);
this.props.onToggle(this.props.member);
};
_highlightName(str: string) {
@ -121,19 +233,37 @@ class DMRoomTile extends React.PureComponent {
}
const avatarSize = 36;
const avatarUrl = getHttpUriForMxc(
MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(),
avatarSize, avatarSize, "crop");
const avatar = this.props.member.isEmail
? <img
src={require("../../../../res/img/icon-email-pill-avatar.svg")}
width={avatarSize} height={avatarSize} />
: <BaseAvatar
url={getHttpUriForMxc(
MatrixClientPeg.get().getHomeserverUrl(), this.props.member.getMxcAvatarUrl(),
avatarSize, avatarSize, "crop")}
name={this.props.member.name}
idName={this.props.member.userId}
width={avatarSize}
height={avatarSize} />;
let checkmark = null;
if (this.props.isSelected) {
// To reduce flickering we put the 'selected' room tile above the real avatar
checkmark = <div className='mx_DMInviteDialog_roomTile_selected' />;
}
// To reduce flickering we put the checkmark on top of the actual avatar (prevents
// the browser from reloading the image source when the avatar remounts).
const stackedAvatar = (
<span className='mx_DMInviteDialog_roomTile_avatarStack'>
{avatar}
{checkmark}
</span>
);
return (
<div className='mx_DMInviteDialog_roomTile' onClick={this._onClick}>
<BaseAvatar
url={avatarUrl}
name={this.props.member.name}
idName={this.props.member.userId}
width={avatarSize}
height={avatarSize}
/>
{stackedAvatar}
<span className='mx_DMInviteDialog_roomTile_name'>{this._highlightName(this.props.member.name)}</span>
<span className='mx_DMInviteDialog_roomTile_userId'>{this._highlightName(this.props.member.userId)}</span>
{timestamp}
@ -149,19 +279,25 @@ export default class DMInviteDialog extends React.PureComponent {
};
_debounceTimer: number = null;
_editorRef: any = null;
constructor() {
super();
this.state = {
targets: [], // string[] of mxids/email addresses
targets: [], // array of Member objects (see interface above)
filterText: "",
recents: this._buildRecents(),
numRecentsShown: INITIAL_ROOMS_SHOWN,
suggestions: this._buildSuggestions(),
numSuggestionsShown: INITIAL_ROOMS_SHOWN,
serverResultsMixin: [], // { user: DirectoryMember, userId: string }[], like recents and suggestions
threepidResultsMixin: [], // { user: ThreepidMember, userId: string}[], like recents and suggestions
canUseIdentityServer: !!MatrixClientPeg.get().getIdentityServerUrl(),
tryingIdentityServer: false,
};
this._editorRef = createRef();
}
_buildRecents(): {userId: string, user: RoomMember, lastActive: number} {
@ -245,7 +381,7 @@ export default class DMInviteDialog extends React.PureComponent {
}
_startDm = () => {
this.props.onFinished(this.state.targets);
this.props.onFinished(this.state.targets.map(t => t.userId));
};
_cancel = () => {
@ -262,7 +398,7 @@ export default class DMInviteDialog extends React.PureComponent {
if (this._debounceTimer) {
clearTimeout(this._debounceTimer);
}
this._debounceTimer = setTimeout(() => {
this._debounceTimer = setTimeout(async () => {
MatrixClientPeg.get().searchUserDirectory({term}).then(r => {
if (term !== this.state.filterText) {
// Discard the results - we were probably too slow on the server-side to make
@ -281,6 +417,62 @@ export default class DMInviteDialog extends React.PureComponent {
console.error(e);
this.setState({serverResultsMixin: []}); // clear results because it's moderately fatal
});
// Whenever we search the directory, also try to search the identity server. It's
// all debounced the same anyways.
if (!this.state.canUseIdentityServer) {
// The user doesn't have an identity server set - warn them of that.
this.setState({tryingIdentityServer: true});
return;
}
if (term.indexOf('@') > 0 && Email.looksValid(term)) {
// Start off by suggesting the plain email while we try and resolve it
// to a real account.
this.setState({
// per above: the userId is a lie here - it's just a regular identifier
threepidResultsMixin: [{user: new ThreepidMember(term), userId: term}],
});
try {
const authClient = new IdentityAuthClient();
const token = await authClient.getAccessToken();
if (term !== this.state.filterText) return; // abandon hope
const lookup = await MatrixClientPeg.get().lookupThreePid(
'email',
term,
undefined, // callback
token,
);
if (term !== this.state.filterText) return; // abandon hope
if (!lookup || !lookup.mxid) {
// We weren't able to find anyone - we're already suggesting the plain email
// as an alternative, so do nothing.
return;
}
// We append the user suggestion to give the user an option to click
// the email anyways, and so we don't cause things to jump around. In
// theory, the user would see the user pop up and think "ah yes, that
// person!"
const profile = await MatrixClientPeg.get().getProfileInfo(lookup.mxid);
if (term !== this.state.filterText || !profile) return; // abandon hope
this.setState({
threepidResultsMixin: [...this.state.threepidResultsMixin, {
user: new DirectoryMember({
user_id: lookup.mxid,
display_name: profile.displayname,
avatar_url: profile.avatar_url,
}),
userId: lookup.mxid,
}],
});
} catch (e) {
console.error("Error searching identity server:");
console.error(e);
this.setState({threepidResultsMixin: []}); // clear results because it's moderately fatal
}
}
}, 150); // 150ms debounce (human reaction time + some)
};
@ -292,14 +484,112 @@ export default class DMInviteDialog extends React.PureComponent {
this.setState({numSuggestionsShown: this.state.numSuggestionsShown + INCREMENT_ROOMS_SHOWN});
};
_toggleMember = (userId) => {
_toggleMember = (member: Member) => {
const targets = this.state.targets.map(t => t); // cheap clone for mutation
const idx = targets.indexOf(userId);
const idx = targets.indexOf(member);
if (idx >= 0) targets.splice(idx, 1);
else targets.push(userId);
else targets.push(member);
this.setState({targets});
};
_removeMember = (member: Member) => {
const targets = this.state.targets.map(t => t); // cheap clone for mutation
const idx = targets.indexOf(member);
if (idx >= 0) {
targets.splice(idx, 1);
this.setState({targets});
}
};
_onPaste = async (e) => {
// Prevent the text being pasted into the textarea
e.preventDefault();
// Process it as a list of addresses to add instead
const text = e.clipboardData.getData("text");
const possibleMembers = [
// If we can avoid hitting the profile endpoint, we should.
...this.state.recents,
...this.state.suggestions,
...this.state.serverResultsMixin,
...this.state.threepidResultsMixin,
];
const toAdd = [];
const failed = [];
const potentialAddresses = text.split(/[\s,]+/);
for (const address of potentialAddresses) {
const member = possibleMembers.find(m => m.userId === address);
if (member) {
toAdd.push(member.user);
continue;
}
if (address.indexOf('@') > 0 && Email.looksValid(address)) {
toAdd.push(new ThreepidMember(address));
continue;
}
if (address[0] !== '@') {
failed.push(address); // not a user ID
continue;
}
try {
const profile = await MatrixClientPeg.get().getProfileInfo(address);
const displayName = profile ? profile.displayname : null;
const avatarUrl = profile ? profile.avatar_url : null;
toAdd.push(new DirectoryMember({
user_id: address,
display_name: displayName,
avatar_url: avatarUrl,
}));
} catch (e) {
console.error("Error looking up profile for " + address);
console.error(e);
failed.push(address);
}
}
if (failed.length > 0) {
const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog');
Modal.createTrackedDialog('Invite Paste Fail', '', QuestionDialog, {
title: _t('Failed to find the following users'),
description: _t(
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
{csvNames: failed.join(", ")},
),
button: _t('OK'),
});
}
this.setState({targets: [...this.state.targets, ...toAdd]});
};
_onClickInputArea = (e) => {
// Stop the browser from highlighting text
e.preventDefault();
e.stopPropagation();
if (this._editorRef && this._editorRef.current) {
this._editorRef.current.focus();
}
};
_onUseDefaultIdentityServerClick = (e) => {
e.preventDefault();
// Update the IS in account data. Actually using it may trigger terms.
// eslint-disable-next-line react-hooks/rules-of-hooks
useDefaultIdentityServer();
this.setState({canUseIdentityServer: true, tryingIdentityServer: false});
};
_onManageSettingsClick = (e) => {
e.preventDefault();
dis.dispatch({ action: 'view_user_settings' });
this._cancel();
};
_renderSection(kind: "recents"|"suggestions") {
let sourceMembers = kind === 'recents' ? this.state.recents : this.state.suggestions;
let showNum = kind === 'recents' ? this.state.numRecentsShown : this.state.numSuggestionsShown;
@ -307,17 +597,27 @@ export default class DMInviteDialog extends React.PureComponent {
const lastActive = (m) => kind === 'recents' ? m.lastActive : null;
const sectionName = kind === 'recents' ? _t("Recent Conversations") : _t("Suggestions");
// Mix in the server results if we have any, but only if we're searching
if (this.state.filterText && this.state.serverResultsMixin && kind === 'suggestions') {
// only pick out the server results that aren't already covered though
const uniqueServerResults = this.state.serverResultsMixin
.filter(u => !sourceMembers.some(m => m.userId === u.userId));
// Mix in the server results if we have any, but only if we're searching. We track the additional
// members separately because we want to filter sourceMembers but trust the mixin arrays to have
// the right members in them.
let additionalMembers = [];
const hasMixins = this.state.serverResultsMixin || this.state.threepidResultsMixin;
if (this.state.filterText && hasMixins && kind === 'suggestions') {
// We don't want to duplicate members though, so just exclude anyone we've already seen.
const notAlreadyExists = (u: Member): boolean => {
return !sourceMembers.some(m => m.userId === u.userId)
&& !additionalMembers.some(m => m.userId === u.userId);
};
sourceMembers = sourceMembers.concat(uniqueServerResults);
const uniqueServerResults = this.state.serverResultsMixin.filter(notAlreadyExists);
additionalMembers = additionalMembers.concat(...uniqueServerResults);
const uniqueThreepidResults = this.state.threepidResultsMixin.filter(notAlreadyExists);
additionalMembers = additionalMembers.concat(...uniqueThreepidResults);
}
// Hide the section if there's nothing to filter by
if (!sourceMembers || sourceMembers.length === 0) return null;
if (sourceMembers.length === 0 && additionalMembers.length === 0) return null;
// Do some simple filtering on the input before going much further. If we get no results, say so.
if (this.state.filterText) {
@ -325,7 +625,7 @@ export default class DMInviteDialog extends React.PureComponent {
sourceMembers = sourceMembers
.filter(m => m.user.name.toLowerCase().includes(filterBy) || m.userId.toLowerCase().includes(filterBy));
if (sourceMembers.length === 0) {
if (sourceMembers.length === 0 && additionalMembers.length === 0) {
return (
<div className='mx_DMInviteDialog_section'>
<h3>{sectionName}</h3>
@ -335,6 +635,10 @@ export default class DMInviteDialog extends React.PureComponent {
}
}
// Now we mix in the additional members. Again, we presume these have already been filtered. We
// also assume they are more relevant than our suggestions and prepend them to the list.
sourceMembers = [...additionalMembers, ...sourceMembers];
// If we're going to hide one member behind 'show more', just use up the space of the button
// with the member's tile instead.
if (showNum === sourceMembers.length - 1) showNum++;
@ -360,6 +664,7 @@ export default class DMInviteDialog extends React.PureComponent {
key={r.userId}
onToggle={this._toggleMember}
highlightWord={this.state.filterText}
isSelected={this.state.targets.some(t => t.userId === r.userId)}
/>
));
return (
@ -371,23 +676,65 @@ export default class DMInviteDialog extends React.PureComponent {
);
}
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const Field = sdk.getComponent("elements.Field");
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
// Dev note: The use of Field is temporary/incomplete pending https://github.com/vector-im/riot-web/issues/11197
// For now, we just list who the targets are.
const editor = (
<div className='mx_DMInviteDialog_editor'>
<Field
id="inviteTargets"
value={this.state.filterText}
onChange={this._updateFilter}
/>
_renderEditor() {
const targets = this.state.targets.map(t => (
<DMUserTile member={t} onRemove={this._removeMember} key={t.userId} />
));
const input = (
<textarea
key={"input"}
rows={1}
onChange={this._updateFilter}
defaultValue={this.state.filterText}
ref={this._editorRef}
onPaste={this._onPaste}
/>
);
return (
<div className='mx_DMInviteDialog_editor' onClick={this._onClickInputArea}>
{targets}
{input}
</div>
);
const targets = this.state.targets.map(t => <div key={t}>{t}</div>);
}
_renderIdentityServerWarning() {
if (!this.state.tryingIdentityServer || this.state.canUseIdentityServer) {
return null;
}
const defaultIdentityServerUrl = getDefaultIdentityServerUrl();
if (defaultIdentityServerUrl) {
return (
<div className="mx_AddressPickerDialog_identityServer">{_t(
"Use an identity server to invite by email. " +
"<default>Use the default (%(defaultIdentityServerName)s)</default> " +
"or manage in <settings>Settings</settings>.",
{
defaultIdentityServerName: abbreviateUrl(defaultIdentityServerUrl),
},
{
default: sub => <a href="#" onClick={this._onUseDefaultIdentityServerClick}>{sub}</a>,
settings: sub => <a href="#" onClick={this._onManageSettingsClick}>{sub}</a>,
},
)}</div>
);
} else {
return (
<div className="mx_AddressPickerDialog_identityServer">{_t(
"Use an identity server to invite by email. " +
"Manage in <settings>Settings</settings>.",
{}, {
settings: sub => <a href="#" onClick={this._onManageSettingsClick}>{sub}</a>,
},
)}</div>
);
}
}
render() {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
const userId = MatrixClientPeg.get().getUserId();
return (
@ -406,9 +753,9 @@ export default class DMInviteDialog extends React.PureComponent {
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>},
)}
</p>
{targets}
<div className='mx_DMInviteDialog_addressBar'>
{editor}
{this._renderEditor()}
{this._renderIdentityServerWarning()}
<AccessibleButton
kind="primary"
onClick={this._startDm}

View File

@ -159,6 +159,14 @@ module.exports = createReactClass({
<E2EIcon status={this.props.e2eStatus} /> :
undefined;
const joinRules = this.props.room && this.props.room.currentState.getStateEvents("m.room.join_rules", "");
const joinRule = joinRules && joinRules.getContent().join_rule;
const joinRuleClass = classNames("mx_RoomHeader_PrivateIcon",
{"mx_RoomHeader_isPrivate": joinRule === "invite"});
const privateIcon = SettingsStore.isFeatureEnabled("feature_cross_signing") ?
<div className={joinRuleClass} /> :
undefined;
if (this.props.onCancelClick) {
cancelButton = <CancelButton onClick={this.props.onCancelClick} />;
}
@ -303,6 +311,7 @@ module.exports = createReactClass({
<div className="mx_RoomHeader_wrapper">
<div className="mx_RoomHeader_avatar">{ roomAvatar }</div>
{ e2eIcon }
{ privateIcon }
{ name }
{ topicElement }
{ cancelButton }

View File

@ -56,7 +56,11 @@ module.exports = createReactClass({
},
getInitialState: function() {
const joinRules = this.props.room.currentState.getStateEvents("m.room.join_rules", "");
const joinRule = joinRules && joinRules.getContent().join_rule;
return ({
joinRule,
hover: false,
badgeHover: false,
contextMenuPosition: null, // DOM bounding box, null if non-shown
@ -104,6 +108,12 @@ module.exports = createReactClass({
});
},
onJoinRule: function(ev) {
if (ev.getType() !== "m.room.join_rules") return;
if (ev.getRoomId() !== this.props.room.roomId) return;
this.setState({ joinRule: ev.getContent().join_rule });
},
onAccountData: function(accountDataEvent) {
if (accountDataEvent.getType() === 'm.push_rules') {
this.setState({
@ -143,6 +153,7 @@ module.exports = createReactClass({
const cli = MatrixClientPeg.get();
cli.on("accountData", this.onAccountData);
cli.on("Room.name", this.onRoomName);
cli.on("RoomState.events", this.onJoinRule);
ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange);
this.dispatcherRef = dis.register(this.onAction);
@ -159,6 +170,7 @@ module.exports = createReactClass({
if (cli) {
MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
MatrixClientPeg.get().removeListener("Room.name", this.onRoomName);
cli.removeListener("RoomState.events", this.onJoinRule);
}
ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange);
dis.unregister(this.dispatcherRef);
@ -292,6 +304,8 @@ module.exports = createReactClass({
const isMenuDisplayed = Boolean(this.state.contextMenuPosition);
const dmUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId);
const classes = classNames({
'mx_RoomTile': true,
'mx_RoomTile_selected': this.state.selected,
@ -303,6 +317,7 @@ module.exports = createReactClass({
'mx_RoomTile_noBadges': !badges,
'mx_RoomTile_transparent': this.props.transparent,
'mx_RoomTile_hasSubtext': subtext && !this.props.collapsed,
'mx_RoomTile_isPrivate': this.state.joinRule == "invite" && !dmUserId,
});
const avatarClasses = classNames({
@ -366,8 +381,6 @@ module.exports = createReactClass({
let ariaLabel = name;
const dmUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId);
let dmIndicator;
let dmOnline;
if (dmUserId) {
@ -381,7 +394,10 @@ module.exports = createReactClass({
const { room } = this.props;
const member = room.getMember(dmUserId);
if (member && member.membership === "join" && room.getJoinedMemberCount() === 2) {
if (
member && member.membership === "join" && room.getJoinedMemberCount() === 2 &&
SettingsStore.isFeatureEnabled("feature_presence_in_room_list")
) {
const UserOnlineDot = sdk.getComponent('rooms.UserOnlineDot');
dmOnline = <UserOnlineDot userId={dmUserId} />;
}
@ -410,6 +426,11 @@ module.exports = createReactClass({
);
}
let privateIcon = null;
if (SettingsStore.isFeatureEnabled("feature_cross_signing")) {
privateIcon = <div className="mx_RoomTile_PrivateIcon" />;
}
return <React.Fragment>
<AccessibleButton
tabIndex="0"
@ -428,6 +449,7 @@ module.exports = createReactClass({
{ dmIndicator }
</div>
</div>
{ privateIcon }
<div className="mx_RoomTile_nameContainer">
<div className="mx_RoomTile_labelContainer">
{ label }

View File

@ -140,7 +140,7 @@ _td("Book");
_td("Pencil");
_td("Paperclip");
_td("Scissors");
_td("Padlock");
_td("Lock");
_td("Key");
_td("Hammer");
_td("Telephone");

View File

@ -2015,5 +2015,22 @@
"Migrate from Key Backup": "Мигрирай от резервно копие на ключове",
"Secure your encrypted messages with a passphrase": "Защитете шифрованите съобщения с парола",
"Storing secrets...": "Складиране на тайни...",
"Unable to set up secret storage": "Неуспешна настройка на секретно складиране"
"Unable to set up secret storage": "Неуспешна настройка на секретно складиране",
"New DM invite dialog (under development)": "Нов процес за канене чрез директни съобщения (все още се разработва)",
"Show info about bridges in room settings": "Показвай информация за връзки с други мрежи в настройките на стаята",
"This bridge was provisioned by <user />": "Тази връзка с друга мрежа е била настроена от <user />",
"This bridge is managed by <user />.": "Тази връзка с друга мрежа се управлява от <user />.",
"Bridged into <channelLink /> <networkLink />, on <protocolName />": "Свързано с <channelLink /><networkLink />, посредством <protocolName />",
"Connected to <channelIcon /> <channelName /> on <networkIcon /> <networkName />": "Свързано с <channelIcon /><channelName /> посредством <networkIcon /><networkName />",
"Connected via %(protocolName)s": "Свързано посредством %(protocolName)s",
"Bridge Info": "Информация за връзката с друга мрежа",
"Below is a list of bridges connected to this room.": "По-долу е списък на връзки с други мрежи свързани с тази стая.",
"Recent Conversations": "Скорошни разговори",
"Suggestions": "Предложения",
"Show more": "Покажи повече",
"Direct Messages": "Директни съобщения",
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "Ако не можете да намерите някой, питайте за потребителското им име, или споделете своето (%(userId)s) или <a>връзка към профила</a>.",
"Go": "Давай",
"Failed to find the following users": "Неуспешно откриване на следните потребители",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Следните потребители не съществуват или са невалидни и не могат да бъдат поканени: %(csvNames)s"
}

View File

@ -1238,7 +1238,7 @@
"Banana": "Banane",
"Apple": "Apfel",
"Strawberry": "Erdbeere",
"Corn": "Weizen",
"Corn": "Mais",
"Pizza": "Pizza",
"Cake": "Kuchen",
"Heart": "Herz",

View File

@ -359,6 +359,7 @@
"Multiple integration managers": "Multiple integration managers",
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
"New DM invite dialog (under development)": "New DM invite dialog (under development)",
"Show a presence dot next to DMs in the room list": "Show a presence dot next to DMs in the room list",
"Enable cross-signing to verify per-user instead of per-device (in development)": "Enable cross-signing to verify per-user instead of per-device (in development)",
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
"Show info about bridges in room settings": "Show info about bridges in room settings",
@ -480,7 +481,7 @@
"Pencil": "Pencil",
"Paperclip": "Paperclip",
"Scissors": "Scissors",
"Padlock": "Padlock",
"Lock": "Lock",
"Key": "Key",
"Hammer": "Hammer",
"Telephone": "Telephone",
@ -1422,6 +1423,8 @@
"View Servers in Room": "View Servers in Room",
"Toolbox": "Toolbox",
"Developer Tools": "Developer Tools",
"Failed to find the following users": "Failed to find the following users",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "The following users might not exist or are invalid, and cannot be invited: %(csvNames)s",
"Recent Conversations": "Recent Conversations",
"Suggestions": "Suggestions",
"Show more": "Show more",
@ -1933,7 +1936,9 @@
"The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.": "The export file will be protected with a passphrase. You should enter the passphrase here, to decrypt the file.",
"File to import": "File to import",
"Import": "Import",
"Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup",
"Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.": "Key Backup is enabled on your account but has not been set up from this session. To set up secret storage, restore your key backup.",
"Restore": "Restore",
"Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup": "Secret Storage will be set up using your existing key backup details. Your secret storage passphrase and recovery key will be the same as they were for your key backup",
"Great! This passphrase looks strong enough.": "Great! This passphrase looks strong enough.",
"<b>Warning</b>: You should only set up secret storage from a trusted computer.": "<b>Warning</b>: You should only set up secret storage from a trusted computer.",
"We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.": "We'll use secret storage to optionally store an encrypted copy of your cross-signing identity for verifying other devices and message keys on our server. Protect your access to encrypted messages with a passphrase to keep it secure.",
@ -1960,6 +1965,7 @@
"Your access to encrypted messages is now protected.": "Your access to encrypted messages is now protected.",
"Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.": "Without setting up secret storage, you won't be able to restore your access to encrypted messages or your cross-signing identity for verifying other devices if you log out or use another device.",
"Set up secret storage": "Set up secret storage",
"Restore your Key Backup": "Restore your Key Backup",
"Migrate from Key Backup": "Migrate from Key Backup",
"Secure your encrypted messages with a passphrase": "Secure your encrypted messages with a passphrase",
"Confirm your passphrase": "Confirm your passphrase",

View File

@ -2032,5 +2032,8 @@
"Recent Conversations": "Azken elkarrizketak",
"Direct Messages": "Mezu zuzenak",
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "Ez baduzu baten bat aurkitzen, eska egiezu bere erabiltzaile-izena, edo eman egiezu zurea (%(userId)s) edo <a>profilera esteka</a>.",
"Go": "Joan"
"Go": "Joan",
"Suggestions": "Proposamenak",
"Failed to find the following users": "Ezin izan dira honako erabiltzaile hauek aurkitu",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Honako erabiltzaile hauek agian ez dira existitzen edo baliogabeak dira, eta ezin dira gonbidatu: %(csvNames)s"
}

View File

@ -2033,5 +2033,8 @@
"Connected to <channelIcon /> <channelName /> on <networkIcon /> <networkName />": "Connecté à <channelIcon /> <channelName /> sur <networkIcon /> <networkName />",
"Connected via %(protocolName)s": "Connecté via %(protocolName)s",
"Bridge Info": "Informations de la passerelle",
"Below is a list of bridges connected to this room.": "Vous trouverez ci-dessous la liste des passerelles connectées à ce salon."
"Below is a list of bridges connected to this room.": "Vous trouverez ci-dessous la liste des passerelles connectées à ce salon.",
"Suggestions": "Suggestions",
"Failed to find the following users": "Impossible de trouver les utilisateurs suivants",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Les utilisateurs suivant nexistent peut-être pas ou ne sont pas valides, et ne peuvent pas être invités : %(csvNames)s"
}

View File

@ -2034,5 +2034,8 @@
"Connected to <channelIcon /> <channelName /> on <networkIcon /> <networkName />": "Ide csatlakozva: <channelIcon /> <channelName /> itt: <networkIcon /> <networkName />",
"Connected via %(protocolName)s": "Ezzel a protokollal csatlakozva: %(protocolName)s",
"Bridge Info": "Híd információ",
"Below is a list of bridges connected to this room.": "Alább látható a lista a szobához kapcsolódó hidakról."
"Below is a list of bridges connected to this room.": "Alább látható a lista a szobához kapcsolódó hidakról.",
"Suggestions": "Javaslatok",
"Failed to find the following users": "Az alábbi felhasználók nem találhatók",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "Az alábbi felhasználók nem léteznek vagy hibásan vannak megadva és nem lehet őket meghívni: %(csvNames)s"
}

View File

@ -2017,5 +2017,20 @@
"The message you are trying to send is too large.": "Il messaggio che stai tentando di inviare è troppo grande.",
"Secret Storage will be set up using your existing key backup details.Your secret storage passphrase and recovery key will be the same as they were for your key backup": "L'archivio segreto verrà impostato usando i dettagli del tuo backup chiavi. La password dell'archivio segreto e la chiave di ripristino saranno le stesse del tuo backup chiavi",
"Migrate from Key Backup": "Migra dal backup chiavi",
"Help": "Aiuto"
"Help": "Aiuto",
"New DM invite dialog (under development)": "Nuovo invito via messaggio diretto (in sviluppo)",
"Show info about bridges in room settings": "Mostra info sui bridge nelle impostazioni stanza",
"This bridge was provisioned by <user />": "Questo bridge è stato fornito da <user />",
"This bridge is managed by <user />.": "Questo bridge è gestito da <user />.",
"Bridged into <channelLink /> <networkLink />, on <protocolName />": "Bridge in <channelLink /> <networkLink />, su <protocolName />",
"Connected to <channelIcon /> <channelName /> on <networkIcon /> <networkName />": "Connesso a <channelIcon /> <channelName /> su <networkIcon /> <networkName />",
"Connected via %(protocolName)s": "Connesso via %(protocolName)s",
"Bridge Info": "Info bridge",
"Below is a list of bridges connected to this room.": "Sotto vedi una lista di brdige connessi a questa stanza.",
"Recent Conversations": "Conversazioni recenti",
"Suggestions": "Suggerimenti",
"Show more": "Mostra altro",
"Direct Messages": "Messaggi diretti",
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "Se non riesci a trovare qualcuno, chiedigli il nome utente o condividi il tuo (%(userId)s) o il <a>link al profilo</a>.",
"Go": "Vai"
}

View File

@ -59,7 +59,7 @@
"You have successfully set a password!": "Jūs sėkmingai įrašėte slaptažodį!",
"Favourite": "Svarbūs",
"All Rooms": "Visi pokalbių kambariai",
"Explore Room State": "Peržiūrėti pokalbių kambario būseną",
"Explore Room State": "Peržiūrėti kambario būseną",
"Source URL": "Šaltinio URL adresas",
"Messages sent by bot": "Roboto siunčiamos žinutės",
"Cancel": "Atšaukti",
@ -303,7 +303,7 @@
"Failed to mute user": "Nepavyko nutildyti naudotoją",
"Are you sure?": "Ar tikrai?",
"Devices": "Įrenginiai",
"Ignore": "Nepaisyti",
"Ignore": "Ignoruoti",
"Invite": "Pakviesti",
"User Options": "Naudotojo parametrai",
"Admin Tools": "Administratoriaus įrankiai",
@ -332,7 +332,7 @@
"Settings": "Nustatymai",
"Community Invites": "Bendruomenės pakvietimai",
"People": "Žmonės",
"%(roomName)s does not exist.": "%(roomName)s nėra.",
"%(roomName)s does not exist.": "%(roomName)s neegzistuoja.",
"%(roomName)s is not accessible at this time.": "%(roomName)s šiuo metu nėra pasiekiamas.",
"Muted Users": "Nutildyti naudotojai",
"Click here to fix": "Spustelėkite čia, norėdami pataisyti",
@ -568,7 +568,7 @@
"Confirm password": "Pakartokite slaptažodį",
"Demote yourself?": "Pažeminti save?",
"Demote": "Pažeminti",
"Share Link to User": "Bendrinti nuorodą su naudotoju",
"Share Link to User": "Dalintis nuoroda į naudotoją",
"Direct chats": "Tiesioginiai pokalbiai",
"The conversation continues here.": "Pokalbis tęsiasi čia.",
"Jump to message": "Pereiti prie žinutės",
@ -644,7 +644,7 @@
"Enable widget screenshots on supported widgets": "Palaikomuose valdikliuose įjungti valdiklių ekrano kopijas",
"Export E2E room keys": "Eksportuoti E2E kambario raktus",
"Last seen": "Paskutinį kartą matytas",
"Unignore": "Nustoti nepaisyti",
"Unignore": "Nebeignoruoti",
"and %(count)s others...|other": "ir %(count)s kitų...",
"and %(count)s others...|one": "ir dar vienas...",
"Mention": "Paminėti",
@ -807,7 +807,7 @@
"Whether or not you're logged in (we don't record your username)": "Nepriklausomai nuo to ar jūs prisijungę (mes neįrašome jūsų vartotojo vardo)",
"Chat with Riot Bot": "Kalbėtis su Riot botu",
"Sign In": "Prisijungti",
"Explore rooms": "Peržiūrėti kambarius",
"Explore rooms": "Žvalgyti kambarius",
"Your Riot is misconfigured": "Jūsų Riot yra neteisingai sukonfigūruotas",
"Sign in to your Matrix account on %(serverName)s": "Prisijunkite prie savo paskyros %(serverName)s serveryje",
"Sign in to your Matrix account on <underlinedServerName />": "Prisijunkite prie savo paskyros <underlinedServerName /> serveryje",
@ -926,5 +926,41 @@
"Group & filter rooms by custom tags (refresh to apply changes)": "Grupuoti ir filtruoti kambarius pagal pasirinktines žymas (atnaujinkite, kad pritaikytumėte pakeitimus)",
"Render simple counters in room header": "Užkrauti paprastus skaitiklius kambario antraštėje",
"Multiple integration managers": "Daugialypiai integracijų valdikliai",
"%(name)s (%(userId)s)": "%(name)s (%(userId)s)"
"%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
"Show info about bridges in room settings": "Rodyti informaciją apie tiltus kambario nustatymuose",
"General": "Bendrieji",
"Remove recent messages by %(user)s": "Pašalinti paskutines %(user)s žinutes",
"Jump to read receipt": "Nušokti iki perskaitytų žinučių",
"Remove recent messages": "Pašalinti paskutines žinutes",
"You can still join it because this is a public room.": "Jūs vis tiek galite prie jo prisijungti, nes tai yra viešas kambarys.",
"Do you want to chat with %(user)s?": "Ar jūs norite kalbėtis su %(user)s?",
"<userName/> wants to chat": "<userName/> nori kalbėtis",
"Start chatting": "Pradėti kalbėtis",
"Do you want to join %(roomName)s?": "Ar jūs norite prisijungti prie %(roomName)s?",
"<userName/> invited you": "<userName/> jus pakvietė",
"You're previewing %(roomName)s. Want to join it?": "Jūs peržiūrite %(roomName)s. Norite prie jo prisijungti?",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s negali būti peržiūrėtas. Ar jūs norite prie jo prisijungti?",
"This room doesn't exist. Are you sure you're at the right place?": "Šis kambarys neegzistuoja. Ar jūs tikras, kad esate tinkamoje vietoje?",
"Create a public room": "Sukurti viešą kambarį",
"Make this room public": "Padaryti šį kambarį viešu",
"Room Settings - %(roomName)s": "Kambario nustatymai - %(roomName)s",
"Upgrade public room": "Atnaujinti viešą kambarį",
"Upload files (%(current)s of %(total)s)": "Įkelti failus (%(current)s iš %(total)s)",
"Upload files": "Įkelti failus",
"Upload all": "Įkelti visus",
"This file is <b>too large</b> to upload. The file size limit is %(limit)s but this file is %(sizeOfThisFile)s.": "Šis failas yra <b>per didelis</b> įkėlimui. Failų dydžio limitas yra %(limit)s, bet šis failas užima %(sizeOfThisFile)s.",
"These files are <b>too large</b> to upload. The file size limit is %(limit)s.": "Šie failai yra <b>per dideli</b> įkėlimui. Failų dydžio limitas yra %(limit)s.",
"Some files are <b>too large</b> to be uploaded. The file size limit is %(limit)s.": "Kai kurie failai yra <b>per dideli</b> įkėlimui. Failų dydžio limitas yra %(limit)s.",
"Upload %(count)s other files|other": "Įkelti %(count)s kitus failus",
"Upload %(count)s other files|one": "Įkelti %(count)s kitą failą",
"Cancel All": "Atšaukti visus",
"Upload Error": "Įkėlimo klaida",
"Explore": "Žvalgyti",
"Filter": "Filtruoti",
"Filter rooms…": "Filtruoti kambarius…",
"This room is not public. You will not be able to rejoin without an invite.": "Šis kambarys nėra viešas. Jūs negalėsite prie jo vėl prisijungti be pakvietimo.",
"Are you sure you want to leave the room '%(roomName)s'?": "Ar tikrai norite palikti kambarį %(roomName)s?",
"%(creator)s created and configured the room.": "%(creator)s sukūrė ir sukonfigūravo kambarį.",
"Riot failed to get the public room list.": "Riot nepavyko gauti viešų kambarių sąrašą.",
"General failure": "Bendras triktis"
}

View File

@ -1267,7 +1267,7 @@
"Unable to load key backup status": "Не удалось получить статус резервного копирования для ключей шифрования",
"Restore from Backup": "Восстановить из резервной копии",
"This device is backing up your keys. ": "Это устройство хранит резервную копию ключей шифрования. ",
"Back up your keys before signing out to avoid losing them.": "Перед выходом сохраните резервную копию ключей шифрования чтобы не потерять их.",
"Back up your keys before signing out to avoid losing them.": "Перед выходом сохраните резервную копию ключей шифрования, чтобы не потерять их.",
"Backup has a signature from <verify>unknown</verify> device with ID %(deviceId)s.": "Резервная копия подписана <verify>неизвестным</verify> устройством с ID %(deviceId)s.",
"Backup has a <validity>valid</validity> signature from this device": "Резервная копия заверена <validity>действительной</validity> подписью от этого устройства",
"Backup has a <validity>valid</validity> signature from <verify>verified</verify> device <device></device>": "Резервная копия заверена <validity>действительной</validity> подписью от <verify>подтверждённого</verify> устройства <device></device>",
@ -1389,7 +1389,7 @@
"Do you want to join %(roomName)s?": "Хотите присоединиться к %(roomName)s?",
"<userName/> invited you": "<userName/> пригласил тебя",
"You're previewing %(roomName)s. Want to join it?": "Вы просматриваете %(roomName)s. Хотите присоединиться?",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s не могут быть предварительно просмотрены. Вы хотите присоединиться к ней?",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s не может быть предварительно просмотрена. Вы хотите присоединиться к ней?",
"This room doesn't exist. Are you sure you're at the right place?": "Эта комната не существует. Вы уверены, что находитесь в правильном месте?",
"Try again later, or ask a room admin to check if you have access.": "Повторите попытку позже или попросите администратора комнаты проверить, есть ли у вас доступ.",
"%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.": "%(errcode)s был возвращен при попытке доступа в комнату. Если вы считаете, что видите это сообщение по ошибке, пожалуйста, <issueLink> отправьте отчет об ошибке </issueLink>.",
@ -1440,7 +1440,7 @@
"You've previously used Riot on %(host)s with lazy loading of members enabled. In this version lazy loading is disabled. As the local cache is not compatible between these two settings, Riot needs to resync your account.": "Ранее вы использовали Riot на %(host)s с отложенной загрузкой участников. В этой версии отложенная загрузка отключена. Поскольку локальный кеш не совместим между этими двумя настройками, Riot необходимо повторно синхронизировать вашу учётную запись.",
"If the other version of Riot is still open in another tab, please close it as using Riot on the same host with both lazy loading enabled and disabled simultaneously will cause issues.": "Если другая версия Riot все еще открыта на другой вкладке, закройте ее, так как использование Riot на том же хосте с включенной и отключенной ленивой загрузкой одновременно вызовет проблемы.",
"Riot now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "Riot теперь использует в 3-5 раз меньше памяти, загружая информацию только о других пользователях, когда это необходимо. Пожалуйста, подождите, пока мы ресинхронизируемся с сервером!",
"I don't want my encrypted messages": "Я не хочу, мои зашифрованные сообщения",
"I don't want my encrypted messages": "Мне не нужны мои зашифрованные сообщения",
"Manually export keys": "Ручной экспорт ключей",
"If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "Если вы столкнулись с какими-либо ошибками или хотите оставить отзыв, которым хотите поделиться, сообщите нам об этом на GitHub.",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "Чтобы избежать повторяющихся проблем, сначала <existingIssuesLink> просмотрите существующие проблемы </existingIssuesLink> (и добавьте +1), либо <newIssueLink> создайте новую проблему </newIssueLink>, если вы не можете ее найти.",
@ -1550,7 +1550,7 @@
"Unable to query for supported registration methods.": "Невозможно запросить поддерживаемые методы регистрации.",
"Great! This passphrase looks strong enough.": "Отлично! Этот пароль выглядит достаточно сильной.",
"We'll store an encrypted copy of your keys on our server. Protect your backup with a passphrase to keep it secure.": "Мы будем хранить зашифрованную копию ваших ключей на нашем сервере. Защитите свою резервную копию паролем, чтобы сохранить ее в безопасности.",
"For maximum security, this should be different from your account password.": "Для максимальной безопасности это должно отличаться от пароля вашей учётной записи.",
"For maximum security, this should be different from your account password.": "Для максимальной безопасности он должен отличаться от пароля вашей учётной записи.",
"Enter a passphrase...": "Введите пароль....",
"Set up with a Recovery Key": "Настройка с ключом восстановления",
"That matches!": "Это совпадает!",

View File

@ -149,7 +149,7 @@
"Incorrect verification code": "Fel verifieringskod",
"Invalid alias format": "Fel alias-format",
"Invalid Email Address": "Ogiltig epostadress",
"Invalid file%(extra)s": "Fel fil%(extra)s",
"Invalid file%(extra)s": "Felaktig fil%(extra)s",
"%(senderName)s invited %(targetName)s.": "%(senderName)s bjöd in %(targetName)s.",
"Invite new room members": "Bjud in nya rumsmedlemmar",
"Invited": "Inbjuden",
@ -1519,5 +1519,130 @@
"%(name)s (%(userId)s)": "%(name)s (%(userId)s)",
"Try out new ways to ignore people (experimental)": "Testa nya sätt att ignorera personer (experimentalt)",
"Use the new, faster, composer for writing messages": "Använd den nya, snabbare kompositören för att skriva meddelanden",
"Show previews/thumbnails for images": "Visa förhandsvisning/tumnagel för bilder"
"Show previews/thumbnails for images": "Visa förhandsvisning/tumnagel för bilder",
"Send cross-signing keys to homeserver": "Skicka korssigneringsnycklar till hemserver",
"Custom (%(level)s)": "Anpassad (%(level)s)",
"Error upgrading room": "Fel vid uppgradering av rum",
"Double check that your server supports the room version chosen and try again.": "Dubbelkolla att din server stöder den valda rumsversionen och försök igen.",
"%(senderName)s placed a voice call.": "%(senderName)s ringde ett röstsamtal.",
"%(senderName)s placed a voice call. (not supported by this browser)": "%(senderName)s ringde ett röstsamtal. (stöds inte av denna webbläsare)",
"%(senderName)s placed a video call.": "%(senderName)s ringde ett videosamtal.",
"%(senderName)s placed a video call. (not supported by this browser)": "%(senderName)s ringde ett videosamtal. (stöds inte av denna webbläsare)",
"%(senderName)s removed the rule banning users matching %(glob)s": "%(senderName)s tog bort regeln som bannar användare som matchar %(glob)s",
"%(senderName)s removed the rule banning rooms matching %(glob)s": "%(senderName)s tog bort regeln som bannar rum som matchar %(glob)s",
"%(senderName)s removed the rule banning servers matching %(glob)s": "%(senderName)s tog bort regeln som bannar servrar som matchar %(glob)s",
"Match system theme": "Matcha systemtema",
"Decline (%(counter)s)": "Avvisa (%(counter)s)",
"on device": "på enhet",
"not found": "hittades inte",
"Connecting to integration manager...": "Ansluter till integrationshanterare...",
"Cannot connect to integration manager": "Det går inte att ansluta till integrationshanterare",
"The integration manager is offline or it cannot reach your homeserver.": "Integrationshanteraren är offline eller kan inte nå din hemserver.",
"Use an Integration Manager <b>(%(serverName)s)</b> to manage bots, widgets, and sticker packs.": "Använd en Integrationshanterare <b>(%(serverName)s)</b> för att hantera bots, widgets och klistermärkespaket.",
"Use an Integration Manager to manage bots, widgets, and sticker packs.": "Använd en Integrationshanterare för att hantera bots, widgets och klistermärkespaket.",
"Manage integrations": "Hantera integrationer",
"Integration Managers receive configuration data, and can modify widgets, send room invites, and set power levels on your behalf.": "Integrationshanterare får konfigurationsdata och kan ändra widgetar, skicka ruminbjudningar och ställa in behörighetsnivåer via ditt konto.",
"Close preview": "Stäng förhandsvisning",
"Room %(name)s": "Rum %(name)s",
"Recent rooms": "Senaste rummen",
"Loading room preview": "Laddar förhandsvisning av rummet",
"Re-join": "Gå med igen",
"Try to join anyway": "Försök att gå med ändå",
"Join the discussion": "Gå med i diskussionen",
"Do you want to chat with %(user)s?": "Vill du chatta med %(user)s?",
"<userName/> wants to chat": "<userName/> vill chatta",
"Start chatting": "Börja chatta",
"Do you want to join %(roomName)s?": "Vill du gå med i %(roomName)s?",
"<userName/> invited you": "<userName/> bjöd in dig",
"You're previewing %(roomName)s. Want to join it?": "Du förhandsgranskar %(roomName)s. Vill du gå med i det?",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s kan inte förhandsvisas. Vill du gå med i det?",
"This room doesn't exist. Are you sure you're at the right place?": "Detta rum finns inte. Är du säker på att du är på rätt plats?",
"Try again later, or ask a room admin to check if you have access.": "Försök igen senare, eller be en rumsadministratör om att kontrollera om du har åtkomst.",
"%(errcode)s was returned while trying to access the room. If you think you're seeing this message in error, please <issueLink>submit a bug report</issueLink>.": "%(errcode)s returnerades när du försökte komma åt rummet. Om du tror att du ser det här meddelandet felaktigt, vänligen <issueLink>skicka in en felrapport</issueLink>.",
"Failed to connect to integration manager": "Det gick inte att ansluta till integrationshanterare",
"React": "Reagera",
"Message Actions": "Meddelandeåtgärder",
"Show image": "Visa bild",
"You have ignored this user, so their message is hidden. <a>Show anyways.</a>": "Du har ignorerat denna användare, så deras meddelande är dolt. <a>Visa ändå.</a>",
"You verified %(name)s": "Du verifierade %(name)s",
"You cancelled verifying %(name)s": "Du avbröt verifiering av %(name)s",
"%(name)s cancelled verifying": "%(name)s avbröt verifiering",
"You accepted": "Du accepterade",
"%(name)s accepted": "%(name)s accepterade",
"You cancelled": "Du avbröt",
"%(name)s cancelled": "%(name)s avbröt",
"%(name)s wants to verify": "%(name)s vill verifiera",
"You sent a verification request": "Du skickade en verifieringsbegäran",
"Reactions": "Reaktioner",
"<reactors/><reactedWith> reacted with %(content)s</reactedWith>": "<reactors/><reactedWith> reagerade med %(content)s</reactedWith>",
"Frequently Used": "Ofta använd",
"Smileys & People": "Smileys & Människor",
"Animals & Nature": "Djur & Natur",
"Food & Drink": "Mat & Dryck",
"Activities": "Aktiviteter",
"Travel & Places": "Resor & Platser",
"Objects": "Objekt",
"Symbols": "Symboler",
"Flags": "Flaggor",
"Quick Reactions": "Snabbreaktioner",
"Cancel search": "Avbryt sökningen",
"Any of the following data may be shared:": "Någon av följande data kan delas:",
"Your display name": "Ditt visningsnamn",
"Your avatar URL": "Din avatar-URL",
"Your user ID": "Ditt användar-ID",
"Your theme": "Ditt tema",
"Riot URL": "Riot-URL",
"Room ID": "Rums-ID",
"Widget ID": "Widget-ID",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s & your Integration Manager.": "Att använda denna widget kan dela data <helpIcon /> med %(widgetDomain)s och din Integrationshanterare.",
"Using this widget may share data <helpIcon /> with %(widgetDomain)s.": "Att använda denna widget kan dela data <helpIcon /> med %(widgetDomain)s.",
"Widgets do not use message encryption.": "Widgets använder inte meddelandekryptering.",
"Widget added by": "Widget tillagd av",
"This widget may use cookies.": "Denna widget kan använda cookies.",
"More options": "Fler alternativ",
"Please <newIssueLink>create a new issue</newIssueLink> on GitHub so that we can investigate this bug.": "Vänligen <newIssueLink>skapa ett nytt ärende</newIssueLink> på GitHub så att vi kan undersöka detta fel.",
"Rotate Left": "Rotera vänster",
"Rotate Right": "Rotera höger",
"Language Dropdown": "Språkmeny",
"%(severalUsers)smade no changes %(count)s times|other": "%(severalUsers)sgjorde inga ändringar %(count)s gånger",
"%(severalUsers)smade no changes %(count)s times|one": "%(severalUsers)sgjorde inga ändringar",
"%(oneUser)smade no changes %(count)s times|other": "%(oneUser)sgjorde inga ändringar %(count)s gånger",
"%(oneUser)smade no changes %(count)s times|one": "%(oneUser)sgjorde inga ändringar",
"Room alias": "Rumsalias",
"e.g. my-room": "t.ex. mitt rum",
"Some characters not allowed": "Vissa tecken är inte tillåtna",
"Please provide a room alias": "Vänligen ange ett rumsalias",
"This alias is available to use": "Detta alias är tillgängligt att använda",
"This alias is already in use": "Detta alias används redan",
"Use an identity server to invite by email. <default>Use the default (%(defaultIdentityServerName)s)</default> or manage in <settings>Settings</settings>.": "Använd en identitetsserver för att bjuda in via epost. <default>Använd standard (%(defaultIdentityServerName)s)</default> eller hantera i <settings>Inställningar</settings>.",
"Use an identity server to invite by email. Manage in <settings>Settings</settings>.": "Använd en identitetsserver för att bjuda in via epost. Hantera i <settings>Inställningar</settings>.",
"Close dialog": "Stäng dialogrutan",
"Please tell us what went wrong or, better, create a GitHub issue that describes the problem.": "Berätta vad som gick fel eller, bättre, skapa ett GitHub-ärende som beskriver problemet.",
"View Servers in Room": "Visa servrar i rum",
"Recent Conversations": "Senaste konversationerna",
"Suggestions": "Förslag",
"Show more": "Visa mer",
"Direct Messages": "Direktmeddelanden",
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "Om det är någon du inte kan hitta, be dem om sitt användarnamn eller dela ditt användarnamn (%(userId)s) eller <a>profillänk</a>.",
"Go": "Gå",
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifiering av den här användaren kommer att markera deras enhet som betrodd, men även markera din enhet som den är betrodd för dem.",
"Waiting for partner to confirm...": "Väntar på att kompanjon ska bekräfta...",
"Incoming Verification Request": "Inkommande verifieringsbegäran",
"Integrations are disabled": "Integrationer är inaktiverade",
"Enable 'Manage Integrations' in Settings to do this.": "Aktivera \"Hantera integrationer\" i Inställningar för att göra detta.",
"Integrations not allowed": "Integrationer inte tillåtna",
"Your Riot doesn't allow you to use an Integration Manager to do this. Please contact an admin.": "Ditt Riot tillåter dig inte att använda en Integrationshanterare för att göra detta. Vänligen kontakta en administratör.",
"Your homeserver doesn't seem to support this feature.": "Din hemserver verkar inte stödja den här funktionen.",
"Message edits": "Meddelandedigeringar",
"Preview": "Förhandsvisa",
"The message you are trying to send is too large.": "Meddelandet du försöker skicka är för stort.",
"New DM invite dialog (under development)": "Ny inbjudningsdialog för direktmeddelanden (under utveckling)",
"Enable cross-signing to verify per-user instead of per-device (in development)": "Aktivera korssignering för att verifiera per användare istället för per enhet (under utveckling)",
"Find others by phone or email": "Hitta andra via telefon eller epost",
"Be found by phone or email": "Bli hittad via telefon eller e-post",
"Terms of Service": "Användarvillkor",
"To continue you need to accept the terms of this service.": "För att fortsätta måste du acceptera villkoren för denna tjänst.",
"Service": "Tjänst",
"Summary": "Sammanfattning",
"Document": "Dokument"
}

View File

@ -1071,5 +1071,231 @@
"Pizza": "Pizza",
"Cake": "Kek",
"Heart": "Kalp",
"Trophy": "Ödül"
"Trophy": "Ödül",
"wait and try again later": "bekle ve tekrar dene",
"Disconnect anyway": "Yinede bağlantıyı kes",
"Go back": "Geri dön",
"Identity Server (%(server)s)": "(%(server)s) Kimlik Sunucusu",
"Identity Server": "Kimlik Sunucusu",
"Do not use an identity server": "Bir kimlik sunucu kullanma",
"Enter a new identity server": "Yeni bir kimlik sunucu gir",
"Change": "Değiştir",
"Manage integrations": "Entegrasyonları yönet",
"Email addresses": "E-posta adresleri",
"Phone numbers": "Telefon numaraları",
"Language and region": "Dil ve bölge",
"Theme": "Tema",
"Account management": "Hesap yönetimi",
"Warning": "Uyarı",
"General": "Genel",
"Discovery": "Keşfet",
"Legal": "Yasal",
"Check for update": "Güncelleme kontrolü",
"Help & About": "Yardım & Hakkında",
"Bug reporting": "Hata raporlama",
"FAQ": "FAQ",
"Versions": "Sürümler",
"Server rules": "Sunucu kuralları",
"User rules": "Kullanıcı kuralları",
"View rules": "Kuralları görüntüle",
"Room list": "Oda listesi",
"Autocomplete delay (ms)": "Oto tamamlama gecikmesi (ms)",
"Key backup": "Anahtar yedek",
"Cross-signing": "Çapraz-imzalama",
"Security & Privacy": "Güvenlik & Gizlilik",
"Learn more about how we use analytics.": "Analizleri nasıl kullandığımız hakkında daha fazla öğrenin.",
"No Audio Outputs detected": "Ses çıkışları tespit edilemedi",
"Audio Output": "Ses Çıkışı",
"Voice & Video": "Ses & Video",
"this room": "bu oda",
"Room information": "Oda bilgisi",
"Room version": "Oda sürümü",
"Room version:": "Oda versiyonu:",
"Developer options": "Geliştirici ayarları",
"Open Devtools": "Geliştirici araçlarını aç",
"Room Addresses": "Oda Adresleri",
"Uploaded sound": "Yüklenen ses",
"Sounds": "Sesler",
"Notification sound": "Bildirim sesi",
"Reset": "Sıfırla",
"Browse": "Gözat",
"Change room name": "Oda adını değiştir",
"Change history visibility": "Geçmiş görünürlüğünü değiştir",
"Change permissions": "İzinleri değiştir",
"Upgrade the room": "Odayı güncelle",
"Enable room encryption": "Oda şifrelemeyi aç",
"Modify widgets": "Görsel bileşenleri düzenle",
"Banned by %(displayName)s": "%(displayName)s tarafından yasaklandı",
"Default role": "Varsayılan rol",
"Send messages": "Mesajları gönder",
"Invite users": "Kullanıcıları davet et",
"Change settings": "Ayarları değiştir",
"Kick users": "Kullanıcıları at",
"Ban users": "Kullanıcıları yasakla",
"Remove messages": "Mesajları sil",
"Notify everyone": "Herkesi bilgilendir",
"Muted Users": "Sessizdeki Kullanıcılar",
"Roles & Permissions": "Roller & İzinler",
"Enable encryption?": "Şifrelemeyi aç?",
"Members only (since they were invited)": "Sadece üyeler (davet edildiklerinden beri)",
"Members only (since they joined)": "Sadece üyeler (katıldıklarından beri)",
"Encryption": "Şifreleme",
"Once enabled, encryption cannot be disabled.": "Açıldıktan donra şifreleme kapatılamaz.",
"Encrypted": "Şifrelenmiş",
"Unable to share email address": "E-posta adresi paylaşılamıyor",
"Your email address hasn't been verified yet": "E-posta adresiniz henüz doğrulanmadı",
"Verify the link in your inbox": "Gelen kutunuzdaki linki doğrulayın",
"Share": "Paylaş",
"Unable to share phone number": "Telefon numarası paylaşılamıyor",
"Unable to verify phone number.": "Telefon numarası doğrulanamıyor.",
"Please enter verification code sent via text.": "Lütfen mesajla gönderilen doğrulama kodunu girin.",
"Verification code": "Doğrulama kodu",
"Remove %(email)s?": "%(email)s sil?",
"Email Address": "E-posta Adresi",
"Remove %(phone)s?": "%(phone)s sil?",
"Phone Number": "Telefon Numarası",
"Cannot add any more widgets": "Daha fazla görsel bileşen eklenemiyor",
"The maximum permitted number of widgets have already been added to this room.": "İzin verilen maksimum sayıda görsel bileşen bu odaya zaten eklenmiş.",
"Add a widget": "Bir görsel bileşen ekle",
"Some devices for this user are not trusted": "Bu kullanıcının bazı cihazları güvenilir değil",
"All devices for this user are trusted": "Bu kullanıcının tüm cihazları güvenilir",
"Some devices in this encrypted room are not trusted": "Bu şifrelenmiş odada bazı cihazlar şifrelenmemiş",
"All devices in this encrypted room are trusted": "Bu şifrelenmiş odadaki tüm cihazlar güvenilir",
"Edit message": "Mesajı düzenle",
"%(senderName)s sent an image": "%(senderName)s bir resim gönderdi",
"%(senderName)s sent a video": "%(senderName)s bir video gönderdi",
"%(senderName)s uploaded a file": "%(senderName)s bir dosya yükledi",
"Key request sent.": "Anahtar isteği gönderildi.",
"This message cannot be decrypted": "Bu mesajın şifresi çözülemedi",
"Unencrypted": "Şifrelenmemiş",
"Close preview": "Önizlemeyi kapat",
"Disinvite this user?": "Bu kullanıcı davetini geri çek?",
"Kick this user?": "Bu kullanıcıyı kov?",
"Unban this user?": "Bu kullanıcının yasağını kaldır?",
"Ban this user?": "Bu kullanıcıyı yasakla?",
"Remove %(count)s messages|other": "%(count)s mesajı sil",
"Remove %(count)s messages|one": "1 mesajı sil",
"There are unknown devices in this room: if you proceed without verifying them, it will be possible for someone to eavesdrop on your call.": "Bu odada bilinmeyen cihazlar var: bunları doğrulamadan devam ederseniz birilerinin çağrılarınıza göz atma ihtimali var.",
"A conference call could not be started because the integrations server is not available": "Entegrasyon sunucusu mevcut olmadığından konferans çağrısı başlatılamadı",
"Send cross-signing keys to homeserver": "Çapraz-imzalama anahtarlarını anasunucuya gönder",
"Warning: any person you add to a community will be publicly visible to anyone who knows the community ID": "Uyarı: Topluluğa eklediğiniz her bir kişi topluluk IDsini bilen herhangi biri tarafından açıkça görünür olacaktır",
"Room name or alias": "Oda adı ve lakabı",
"Failed to invite users to %(groupId)s": "%(groupId)s grubuna kullanıcı daveti başarısız",
"Failed to add the following rooms to %(groupId)s:": "%(groupId)s odalarına ekleme başarısız:",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Düz-metin mesajına ¯\\_(ツ)_/¯ ifadesi ekler",
"Rooster": "Horoz",
"Cross-signing public keys:": "Çarpraz-imzalama açık anahtarları:",
"Cross-signing private keys:": "Çarpraz-imzalama gizli anahtarları:",
"Backup has a <validity>valid</validity> signature from this user": "Yedek bu kullanıcıdan <validity>geçerli</validity> anahtara sahip",
"Backup has a <validity>invalid</validity> signature from this user": "Yedek bu kullanıcıdan <validity>geçersiz</validity> bir anahtara sahip",
"Add an email address to configure email notifications": "E-posta bildirimlerini yapılandırmak için bir e-posta adresi ekleyin",
"Identity Server URL must be HTTPS": "Kimlik Sunucu URL adresi HTTPS olmak zorunda",
"Not a valid Identity Server (status code %(code)s)": "Geçerli bir Kimlik Sunucu değil ( durum kodu %(code)s )",
"Terms of service not accepted or the identity server is invalid.": "Hizmet şartları kabuk edilmedi yada kimlik sunucu geçersiz.",
"The identity server you have chosen does not have any terms of service.": "Seçtiğiniz kimlik sunucu herhangi bir hizmet şartları sözleşmesine sahip değil.",
"Disconnect identity server": "Kimlik sunucu bağlantısını kes",
"Disconnect from the identity server <idserver />?": "<idserver /> kimlik sunucusundan bağlantıyı kes?",
"Disconnect": "Bağlantıyı kes",
"contact the administrators of identity server <idserver />": "<idserver /> kimlik sunucusu yöneticisiyle bağlantıya geç",
"Deactivate user?": "Kullanıcıyı pasifleştir?",
"Deactivate user": "Kullanıcıyı pasifleştir",
"Failed to deactivate user": "Kullanıcı pasifleştirme başarısız",
"Invite": "Davet",
"Share Link to User": "Kullanıcıya Link Paylaş",
"User Options": "Kullanıcı Ayarları",
"Send an encrypted reply…": "Şifrelenmiş bir cevap gönder…",
"Send a reply (unencrypted)…": "Bir cevap gönder (şifresiz)…",
"Send an encrypted message…": "Şifreli bir mesaj gönder…",
"Send a message (unencrypted)…": "Bir mesaj gönder (şifresiz)…",
"Bold": "Kalın",
"Italics": "Eğik",
"Code block": "Kod bloku",
"No pinned messages.": "Sabitlenmiş mesaj yok.",
"Loading...": "Yükleniyor...",
"Pinned Messages": "Sabitlenmiş Mesajlar",
"Jump to message": "Mesaja git",
"%(duration)ss": "%(duration)ssn",
"%(duration)sm": "%(duration)sdk",
"%(duration)sh": "%(duration)ssa",
"%(duration)sd": "%(duration)sgün",
"Online for %(duration)s": "%(duration)s süresince çevrimiçi",
"Offline for %(duration)s": "%(duration)s süresince çevrimdışı",
"Unknown for %(duration)s": "%(duration)s süresince bilinmiyor",
"Unknown": "Bilinmeyen",
"Replying": "Cevap yazıyor",
"Room %(name)s": "Oda %(name)s",
"Share room": "Oda paylaş",
"Community Invites": "Topluluk Davetleri",
"System Alerts": "Sistem Uyarıları",
"Joining room …": "Odaya giriliyor …",
"Loading …": "Yükleniyor …",
"Rejecting invite …": "Davet reddediliyor …",
"Join the conversation with an account": "Konuşmaya bir hesapla katıl",
"Sign Up": "Kayıt Ol",
"Sign In": "Giriş Yap",
"Loading room preview": "Oda önizlemesi yükleniyor",
"Reason: %(reason)s": "Sebep: %(reason)s",
"Forget this room": "Bu odayı unut",
"Re-join": "Yeniden katıl",
"Join the discussion": "Tartışmaya katıl",
"Do you want to chat with %(user)s?": "%(user)s ile sohbet etmek ister misin?",
"<userName/> wants to chat": "<userName/> sohbet etmek istiyor",
"Start chatting": "Sohbet başlat",
"Do you want to join %(roomName)s?": "%(roomName)s odasına katılmak ister misin?",
"<userName/> invited you": "<userName/> davet etti",
"%(roomName)s can't be previewed. Do you want to join it?": "%(roomName)s odasında önizleme yapılamaz. Katılmak ister misin?",
"This room doesn't exist. Are you sure you're at the right place?": "Bu oda mevcut değil. Doğru yerde olduğunuza emin misiniz?",
"Try again later, or ask a room admin to check if you have access.": "Daha sonra deneyin, yada bir oda adminine erişiminiz olup olmadığını sorun.",
"Never lose encrypted messages": "Şifrelenmiş mesajları asla kaybetmeyin",
"Not now": "Şimdi değil",
"Don't ask me again": "Yeniden sorma",
"%(count)s unread messages.|other": "%(count)s okunmamış mesaj.",
"%(count)s unread messages.|one": "1 okunmamış mesaj.",
"Unread messages.": "Okunmamış mesajlar.",
"This room has already been upgraded.": "Bu ıda zaten güncellenmiş.",
"Only room administrators will see this warning": "Bu uyarıyı sadece oda yöneticileri görür",
"Failed to connect to integration manager": "Entegrasyon yöneticisine bağlanma başarısız",
"Add some now": "Şimdi biraz ekle",
"Stickerpack": ıkartma paketi",
"Hide Stickers": ıkartmaları Gizle",
"Show Stickers": ıkartmaları Göster",
"Failed to revoke invite": "Davetin geri çekilmesi başarısız",
"Revoke invite": "Davet geri çekildi",
"Invited by %(sender)s": "%(sender)s tarafından davet",
"Error updating main address": "Ana adres güncellemede hata",
"Error removing alias": "Lakap silmede hata",
"Main address": "Ana adres",
"Invalid community ID": "Geçersiz topluluk ID si",
"'%(groupId)s' is not a valid community ID": "%(groupId)s geçerli olmayan bir topluluk ID si",
"New community ID (e.g. +foo:%(localDomain)s)": "Yeni topluluk ID si ( örn. +foo:%(localDomain)s)",
"Room Name": "Oda Adı",
"Hide verified sessions": "Onaylı oturumları gizle",
"%(count)s verified sessions|other": "%(count)s doğrulanmış oturum",
"%(count)s verified sessions|one": "1 doğrulanmış oturum",
"Direct message": "Doğrudan mesaj",
"Remove from community": "Topluluktan sil",
"Remove this user from community?": "Bu kullanıcıyı topluluktan sil?",
"Failed to withdraw invitation": "Davetin geri çekilmesi başarısız",
"Failed to remove user from community": "Kullanıcıyı bu topluluktan silme başarısız",
"Verify": "Doğrula",
"Security": "Güvenlik",
"Reply": "Cevapla",
"Edit": "Düzenle",
"Message Actions": "Mesaj Eylemleri",
"Show image": "Resim göster",
"You verified %(name)s": "%(name)s yı doğruladınız",
"You cancelled verifying %(name)s": "%(name)s doğrulaması iptal edildi",
"You accepted": "Kabul ettiniz",
"%(name)s accepted": "%(name)s kabul etti",
"You cancelled": "İptal ettiniz",
"%(name)s cancelled": "%(name)s iptal etti",
"%(name)s wants to verify": "%(name)s doğrulamak istiyor",
"You sent a verification request": "Doğrulama isteği gönderdiniz",
"Show all": "Hepsini göster",
"Reactions": "Tepkiler",
"Click here to see older messages.": "Daha eski mesajları görmek için buraya tıklayın.",
"Copied!": "Kopyalandı!",
"Failed to copy": "Kopyalama başarısız",
"edited": "düzenlendi",
"Message removed by %(userId)s": "Mesaj %(userId)s tarafından silindi"
}

View File

@ -2033,5 +2033,8 @@
"Connected to <channelIcon /> <channelName /> on <networkIcon /> <networkName />": "連線到 <channelIcon /> <channelName /> 於 <networkIcon /> <networkName />",
"Connected via %(protocolName)s": "透過 %(protocolName)s 連線",
"Bridge Info": "橋接資訊",
"Below is a list of bridges connected to this room.": "以下是連線到此聊天室的橋接列表。"
"Below is a list of bridges connected to this room.": "以下是連線到此聊天室的橋接列表。",
"Suggestions": "建議",
"Failed to find the following users": "找不到以下使用者",
"The following users might not exist or are invalid, and cannot be invited: %(csvNames)s": "以下使用者可能不存在或無效,且無法被邀請:%(csvNames)s"
}

View File

@ -134,6 +134,12 @@ export const SETTINGS = {
supportedLevels: LEVELS_FEATURE,
default: false,
},
"feature_presence_in_room_list": {
isFeature: true,
displayName: _td("Show a presence dot next to DMs in the room list"),
supportedLevels: LEVELS_FEATURE,
default: false,
},
"mjolnirRooms": {
supportedLevels: ['account'],
default: [],

View File

@ -5509,9 +5509,10 @@ mathml-tag-names@^2.0.1:
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.1.tgz#6dff66c99d55ecf739ca53c492e626f1d12a33cc"
integrity sha512-pWB896KPGSGkp1XtyzRBftpTzwSOL0Gfk0wLvxt4f2mgzjY19o0LxJ3U25vNWTzsh7da+KTbuXQoQ3lOJZ8WHw==
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop":
version "2.4.6"
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/01f0dd4498fb689cb66091aff7aa0ae49f9b8ebf"
matrix-js-sdk@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-3.0.0.tgz#97908f9eda9eeb3ba0333b7e474c45f2b258e50c"
integrity sha512-lzUMwJAZHw7Dk0K+rubqe6kEpy4+pJ+qCp8n6lisfdKfMDJXdNCkjiiXRnakM1ZD4PFYK8ju89+NfxlyhAAd4A==
dependencies:
another-json "^0.2.0"
browser-request "^0.3.3"