Initial implementation of FTUE user lists design
This covers the "recents" section and rough design exclusively. It is known that the Field does nothing and that there's a bunch of missing functionality - this is to be iterated upon in future PRs. Labs flag is to aide development and should be removed in a very near future PR. Also, this is focusing on DMs and not user lists in general because I misinterpreted the scope. I'll fix this in a future PR and instead make this the best DM invite dialog it can be. Closes https://github.com/vector-im/riot-web/issues/11197
This commit is contained in:
parent
560cff0ae1
commit
4de0f7257a
11 changed files with 342 additions and 2 deletions
|
@ -74,7 +74,6 @@
|
||||||
"file-saver": "^1.3.3",
|
"file-saver": "^1.3.3",
|
||||||
"filesize": "3.5.6",
|
"filesize": "3.5.6",
|
||||||
"flux": "2.1.1",
|
"flux": "2.1.1",
|
||||||
"react-focus-lock": "^2.2.1",
|
|
||||||
"focus-visible": "^5.0.2",
|
"focus-visible": "^5.0.2",
|
||||||
"fuse.js": "^2.2.0",
|
"fuse.js": "^2.2.0",
|
||||||
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
"gemini-scrollbar": "github:matrix-org/gemini-scrollbar#91e1e566",
|
||||||
|
@ -82,6 +81,7 @@
|
||||||
"glob": "^5.0.14",
|
"glob": "^5.0.14",
|
||||||
"glob-to-regexp": "^0.4.1",
|
"glob-to-regexp": "^0.4.1",
|
||||||
"highlight.js": "^9.15.8",
|
"highlight.js": "^9.15.8",
|
||||||
|
"humanize": "^0.0.9",
|
||||||
"is-ip": "^2.0.0",
|
"is-ip": "^2.0.0",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^2.2.1",
|
||||||
"linkifyjs": "^2.1.6",
|
"linkifyjs": "^2.1.6",
|
||||||
|
@ -99,6 +99,7 @@
|
||||||
"react-addons-css-transition-group": "15.6.2",
|
"react-addons-css-transition-group": "15.6.2",
|
||||||
"react-beautiful-dnd": "^4.0.1",
|
"react-beautiful-dnd": "^4.0.1",
|
||||||
"react-dom": "^16.9.0",
|
"react-dom": "^16.9.0",
|
||||||
|
"react-focus-lock": "^2.2.1",
|
||||||
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
|
"react-gemini-scrollbar": "github:matrix-org/react-gemini-scrollbar#9cf17f63b7c0b0ec5f31df27da0f82f7238dc594",
|
||||||
"resize-observer-polyfill": "^1.5.0",
|
"resize-observer-polyfill": "^1.5.0",
|
||||||
"sanitize-html": "^1.18.4",
|
"sanitize-html": "^1.18.4",
|
||||||
|
|
|
@ -56,6 +56,7 @@
|
||||||
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
@import "./views/dialogs/_ConfirmUserActionDialog.scss";
|
||||||
@import "./views/dialogs/_CreateGroupDialog.scss";
|
@import "./views/dialogs/_CreateGroupDialog.scss";
|
||||||
@import "./views/dialogs/_CreateRoomDialog.scss";
|
@import "./views/dialogs/_CreateRoomDialog.scss";
|
||||||
|
@import "./views/dialogs/_DMInviteDialog.scss";
|
||||||
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
@import "./views/dialogs/_DeactivateAccountDialog.scss";
|
||||||
@import "./views/dialogs/_DeviceVerifyDialog.scss";
|
@import "./views/dialogs/_DeviceVerifyDialog.scss";
|
||||||
@import "./views/dialogs/_DevtoolsDialog.scss";
|
@import "./views/dialogs/_DevtoolsDialog.scss";
|
||||||
|
|
81
res/css/views/dialogs/_DMInviteDialog.scss
Normal file
81
res/css/views/dialogs/_DMInviteDialog.scss
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_addressBar {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_editor {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%; // Needed to make the Field inside grow
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_Field {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_goButton {
|
||||||
|
width: 48px;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_section {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
font-weight: bold;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $user-tile-hover-bg-color;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
color: $primary-fg-color;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_userId {
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
margin-left: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_DMInviteDialog_roomTile_time {
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: $muted-fg-color;
|
||||||
|
float: right;
|
||||||
|
line-height: 36px; // Height of the avatar to keep the time vertically aligned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ $room-highlight-color: #343a46;
|
||||||
// typical text (dark-on-white in light skin)
|
// typical text (dark-on-white in light skin)
|
||||||
$primary-fg-color: $text-primary-color;
|
$primary-fg-color: $text-primary-color;
|
||||||
$primary-bg-color: $bg-color;
|
$primary-bg-color: $bg-color;
|
||||||
|
$muted-fg-color: $header-panel-text-primary-color;
|
||||||
|
|
||||||
// used for dialog box text
|
// used for dialog box text
|
||||||
$light-fg-color: $header-panel-text-secondary-color;
|
$light-fg-color: $header-panel-text-secondary-color;
|
||||||
|
@ -172,6 +173,8 @@ $interactive-tooltip-fg-color: #ffffff;
|
||||||
|
|
||||||
$breadcrumb-placeholder-bg-color: #272c35;
|
$breadcrumb-placeholder-bg-color: #272c35;
|
||||||
|
|
||||||
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
// ***** Mixins! *****
|
// ***** Mixins! *****
|
||||||
|
|
||||||
@define-mixin mx_DialogButton {
|
@define-mixin mx_DialogButton {
|
||||||
|
|
|
@ -21,6 +21,7 @@ $header-panel-bg-color: #f3f8fd;
|
||||||
// typical text (dark-on-white in light skin)
|
// typical text (dark-on-white in light skin)
|
||||||
$primary-fg-color: #2e2f32;
|
$primary-fg-color: #2e2f32;
|
||||||
$primary-bg-color: #ffffff;
|
$primary-bg-color: #ffffff;
|
||||||
|
$muted-fg-color: #61708b; // Commonly used in headings and relevant alt text
|
||||||
|
|
||||||
// used for dialog box text
|
// used for dialog box text
|
||||||
$light-fg-color: #747474;
|
$light-fg-color: #747474;
|
||||||
|
@ -293,6 +294,8 @@ $interactive-tooltip-fg-color: #ffffff;
|
||||||
|
|
||||||
$breadcrumb-placeholder-bg-color: #e8eef5;
|
$breadcrumb-placeholder-bg-color: #e8eef5;
|
||||||
|
|
||||||
|
$user-tile-hover-bg-color: $header-panel-bg-color;
|
||||||
|
|
||||||
// ***** Mixins! *****
|
// ***** Mixins! *****
|
||||||
|
|
||||||
@define-mixin mx_DialogButton {
|
@define-mixin mx_DialogButton {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import sdk from './';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import DMRoomMap from './utils/DMRoomMap';
|
import DMRoomMap from './utils/DMRoomMap';
|
||||||
import { _t } from './languageHandler';
|
import { _t } from './languageHandler';
|
||||||
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invites multiple addresses to a room
|
* Invites multiple addresses to a room
|
||||||
|
@ -41,6 +42,18 @@ function inviteMultipleToRoom(roomId, addrs) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function showStartChatInviteDialog() {
|
export function showStartChatInviteDialog() {
|
||||||
|
if (SettingsStore.isFeatureEnabled("feature_ftue_dms")) {
|
||||||
|
const DMInviteDialog = sdk.getComponent("dialogs.DMInviteDialog");
|
||||||
|
Modal.createTrackedDialog('Start DM', '', DMInviteDialog, {
|
||||||
|
onFinished: (inviteIds) => {
|
||||||
|
// TODO: Replace _onStartDmFinished with less hacks
|
||||||
|
if (inviteIds.length > 0) _onStartDmFinished(true, inviteIds.map(i => ({address: i})));
|
||||||
|
// else ignore and just do nothing
|
||||||
|
},
|
||||||
|
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
const AddressPickerDialog = sdk.getComponent("dialogs.AddressPickerDialog");
|
||||||
|
|
||||||
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
|
Modal.createTrackedDialog('Start a chat', '', AddressPickerDialog, {
|
||||||
|
@ -99,7 +112,7 @@ export function isValid3pidInvite(event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Immutable DMs replaces this
|
// TODO: Canonical DMs replaces this
|
||||||
function _onStartDmFinished(shouldInvite, addrs) {
|
function _onStartDmFinished(shouldInvite, addrs) {
|
||||||
if (!shouldInvite) return;
|
if (!shouldInvite) return;
|
||||||
|
|
||||||
|
|
212
src/components/views/dialogs/DMInviteDialog.js
Normal file
212
src/components/views/dialogs/DMInviteDialog.js
Normal file
|
@ -0,0 +1,212 @@
|
||||||
|
/*
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {_t} from "../../../languageHandler";
|
||||||
|
import sdk from "../../../index";
|
||||||
|
import MatrixClientPeg from "../../../MatrixClientPeg";
|
||||||
|
import {makeUserPermalink} from "../../../utils/permalinks/Permalinks";
|
||||||
|
import DMRoomMap from "../../../utils/DMRoomMap";
|
||||||
|
import {RoomMember} from "matrix-js-sdk/lib/matrix";
|
||||||
|
import * as humanize from "humanize";
|
||||||
|
|
||||||
|
// 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 DMRoomTile extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
member: PropTypes.object.isRequired,
|
||||||
|
lastActiveTs: PropTypes.number,
|
||||||
|
onToggle: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
_onClick = (e) => {
|
||||||
|
// Stop the browser from highlighting text
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
this.props.onToggle(this.props.member.userId);
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const MemberAvatar = sdk.getComponent("views.avatars.MemberAvatar");
|
||||||
|
|
||||||
|
let timestamp = null;
|
||||||
|
if (this.props.lastActiveTs) {
|
||||||
|
// TODO: [TravisR] Figure out how to i18n this
|
||||||
|
// `humanize` wants seconds for a timestamp, so divide by 1000
|
||||||
|
const humanTs = humanize.relativeTime(this.props.lastActiveTs / 1000);
|
||||||
|
timestamp = <span className='mx_DMInviteDialog_roomTile_time'>{humanTs}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mx_DMInviteDialog_roomTile' onClick={this._onClick}>
|
||||||
|
<MemberAvatar member={this.props.member} width={36} height={36} />
|
||||||
|
<span className='mx_DMInviteDialog_roomTile_name'>{this.props.member.name}</span>
|
||||||
|
<span className='mx_DMInviteDialog_roomTile_userId'>{this.props.member.userId}</span>
|
||||||
|
{timestamp}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class DMInviteDialog extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
// Takes an array of user IDs/emails to invite.
|
||||||
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
targets: [], // string[] of mxids/email addresses
|
||||||
|
filterText: "",
|
||||||
|
recents: this._buildRecents(),
|
||||||
|
numRecentsShown: INITIAL_ROOMS_SHOWN,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildRecents(): {userId: string, user: RoomMember, lastActive: number} {
|
||||||
|
const rooms = DMRoomMap.shared().getUniqueRoomsWithIndividuals();
|
||||||
|
const recents = [];
|
||||||
|
for (const userId in rooms) {
|
||||||
|
const room = rooms[userId];
|
||||||
|
const member = room.getMember(userId);
|
||||||
|
if (!member) continue; // just skip people who don't have memberships for some reason
|
||||||
|
|
||||||
|
const lastEventTs = room.timeline && room.timeline.length ? room.timeline[room.timeline.length - 1].getTs() : 0;
|
||||||
|
if (!lastEventTs) continue; // something weird is going on with this room
|
||||||
|
|
||||||
|
recents.push({userId, user: member, lastActive: lastEventTs});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the recents by last active to save us time later
|
||||||
|
recents.sort((a, b) => b.lastActive - a.lastActive);
|
||||||
|
|
||||||
|
return recents;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startDm = () => {
|
||||||
|
this.props.onFinished(this.state.targets);
|
||||||
|
};
|
||||||
|
|
||||||
|
_cancel = () => {
|
||||||
|
this.props.onFinished([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
_updateFilter = (e) => {
|
||||||
|
this.setState({filterText: e.target.value});
|
||||||
|
};
|
||||||
|
|
||||||
|
_showMoreRecents = () => {
|
||||||
|
this.setState({numRecentsShown: this.state.numRecentsShown + INCREMENT_ROOMS_SHOWN});
|
||||||
|
};
|
||||||
|
|
||||||
|
_toggleMember = (userId) => {
|
||||||
|
const targets = this.state.targets.map(t => t); // cheap clone for mutation
|
||||||
|
const idx = targets.indexOf(userId);
|
||||||
|
if (idx >= 0) targets.splice(idx, 1);
|
||||||
|
else targets.push(userId);
|
||||||
|
this.setState({targets});
|
||||||
|
};
|
||||||
|
|
||||||
|
_renderRecents() {
|
||||||
|
if (!this.state.recents || this.state.recents.length === 0) return null;
|
||||||
|
|
||||||
|
// .slice() will return an incomplete array but won't error on us if we go too far
|
||||||
|
const toRender = this.state.recents.slice(0, this.state.numRecentsShown);
|
||||||
|
const hasMore = toRender.length < this.state.recents.length;
|
||||||
|
|
||||||
|
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
|
||||||
|
let showMore = null;
|
||||||
|
if (hasMore) {
|
||||||
|
showMore = (
|
||||||
|
<AccessibleButton onClick={this._showMoreRecents} kind="link">
|
||||||
|
{_t("Show more")}
|
||||||
|
</AccessibleButton>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='mx_DMInviteDialog_section'>
|
||||||
|
<h3>{_t("Recent Conversations")}</h3>
|
||||||
|
{toRender.map(r => <DMRoomTile member={r.user} lastActiveTs={r.lastActive} key={r.userId} onToggle={this._toggleMember} />)}
|
||||||
|
{showMore}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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}
|
||||||
|
placeholder="TODO: Implement filtering/searching (https://github.com/vector-im/riot-web/issues/11199)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
const targets = this.state.targets.map(t => <div key={t}>{t}</div>);
|
||||||
|
|
||||||
|
const userId = MatrixClientPeg.get().getUserId();
|
||||||
|
return (
|
||||||
|
<BaseDialog
|
||||||
|
className='mx_DMInviteDialog'
|
||||||
|
hasCancel={true}
|
||||||
|
onFinished={this._cancel}
|
||||||
|
title={_t("Direct Messages")}
|
||||||
|
>
|
||||||
|
<div className='mx_DMInviteDialog_content'>
|
||||||
|
<p>
|
||||||
|
{_t(
|
||||||
|
"If you can't find someone, ask them for their username, or share your " +
|
||||||
|
"username (%(userId)s) or <a>profile link</a>.",
|
||||||
|
{userId},
|
||||||
|
{a: (sub) => <a href={makeUserPermalink(userId)} rel="noopener" target="_blank">{sub}</a>},
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
{targets}
|
||||||
|
<div className='mx_DMInviteDialog_addressBar'>
|
||||||
|
{editor}
|
||||||
|
<AccessibleButton
|
||||||
|
kind="primary"
|
||||||
|
onClick={this._startDm}
|
||||||
|
className='mx_DMInviteDialog_goButton'
|
||||||
|
>
|
||||||
|
{_t("Go")}
|
||||||
|
</AccessibleButton>
|
||||||
|
</div>
|
||||||
|
{this._renderRecents()}
|
||||||
|
</div>
|
||||||
|
</BaseDialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -358,6 +358,7 @@
|
||||||
"Render simple counters in room header": "Render simple counters in room header",
|
"Render simple counters in room header": "Render simple counters in room header",
|
||||||
"Multiple integration managers": "Multiple integration managers",
|
"Multiple integration managers": "Multiple integration managers",
|
||||||
"Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)",
|
"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)",
|
||||||
"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 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)",
|
"Enable local event indexing and E2EE search (requires restart)": "Enable local event indexing and E2EE search (requires restart)",
|
||||||
"Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages",
|
"Use the new, faster, composer for writing messages": "Use the new, faster, composer for writing messages",
|
||||||
|
@ -1431,6 +1432,11 @@
|
||||||
"View Servers in Room": "View Servers in Room",
|
"View Servers in Room": "View Servers in Room",
|
||||||
"Toolbox": "Toolbox",
|
"Toolbox": "Toolbox",
|
||||||
"Developer Tools": "Developer Tools",
|
"Developer Tools": "Developer Tools",
|
||||||
|
"Show more": "Show more",
|
||||||
|
"Recent Conversations": "Recent Conversations",
|
||||||
|
"Direct Messages": "Direct Messages",
|
||||||
|
"If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.": "If you can't find someone, ask them for their username, or share your username (%(userId)s) or <a>profile link</a>.",
|
||||||
|
"Go": "Go",
|
||||||
"An error has occurred.": "An error has occurred.",
|
"An error has occurred.": "An error has occurred.",
|
||||||
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
|
"Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.": "Verify this user to mark them as trusted. Trusting users gives you extra peace of mind when using end-to-end encrypted messages.",
|
||||||
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.",
|
"Verifying this user will mark their device as trusted, and also mark your device as trusted to them.": "Verifying this user will mark their device as trusted, and also mark your device as trusted to them.",
|
||||||
|
|
|
@ -128,6 +128,12 @@ export const SETTINGS = {
|
||||||
supportedLevels: LEVELS_FEATURE,
|
supportedLevels: LEVELS_FEATURE,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
"feature_ftue_dms": {
|
||||||
|
isFeature: true,
|
||||||
|
displayName: _td("New DM invite dialog (under development)"),
|
||||||
|
supportedLevels: LEVELS_FEATURE,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
"mjolnirRooms": {
|
"mjolnirRooms": {
|
||||||
supportedLevels: ['account'],
|
supportedLevels: ['account'],
|
||||||
default: [],
|
default: [],
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016 OpenMarket Ltd
|
||||||
|
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,6 +17,7 @@ limitations under the License.
|
||||||
|
|
||||||
import MatrixClientPeg from '../MatrixClientPeg';
|
import MatrixClientPeg from '../MatrixClientPeg';
|
||||||
import _uniq from 'lodash/uniq';
|
import _uniq from 'lodash/uniq';
|
||||||
|
import {Room} from "matrix-js-sdk/lib/matrix";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class that takes a Matrix Client and flips the m.direct map
|
* Class that takes a Matrix Client and flips the m.direct map
|
||||||
|
@ -144,6 +146,13 @@ export default class DMRoomMap {
|
||||||
return this.roomToUser[roomId];
|
return this.roomToUser[roomId];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getUniqueRoomsWithIndividuals(): {[userId: string]: Room} {
|
||||||
|
return Object.keys(this.roomToUser)
|
||||||
|
.map(r => ({userId: this.getUserIdForRoomId(r), room: this.matrixClient.getRoom(r)}))
|
||||||
|
.filter(r => r.userId && r.room && r.room.getInvitedAndJoinedMemberCount() === 2)
|
||||||
|
.reduce((obj, r) => (obj[r.userId] = r.room) && obj, {});
|
||||||
|
}
|
||||||
|
|
||||||
_getUserToRooms() {
|
_getUserToRooms() {
|
||||||
if (!this.userToRooms) {
|
if (!this.userToRooms) {
|
||||||
const userToRooms = this.mDirectEvent;
|
const userToRooms = this.mDirectEvent;
|
||||||
|
|
|
@ -4089,6 +4089,11 @@ humanize-ms@^1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "^2.0.0"
|
ms "^2.0.0"
|
||||||
|
|
||||||
|
humanize@^0.0.9:
|
||||||
|
version "0.0.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/humanize/-/humanize-0.0.9.tgz#1994ffaecdfe9c441ed2bdac7452b7bb4c9e41a4"
|
||||||
|
integrity sha1-GZT/rs3+nEQe0r2sdFK3u0yeQaQ=
|
||||||
|
|
||||||
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
|
||||||
version "0.4.24"
|
version "0.4.24"
|
||||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
|
||||||
|
|
Loading…
Reference in a new issue