diff --git a/src/RoomInvite.js b/src/RoomInvite.js
index 1979c6d111..31541148d9 100644
--- a/src/RoomInvite.js
+++ b/src/RoomInvite.js
@@ -85,9 +85,7 @@ function _onStartChatFinished(shouldInvite, addrs) {
if (rooms.length > 0) {
// A Direct Message room already exists for this user, so select a
// room from a list that is similar to the one in MemberInfo panel
- const ChatCreateOrReuseDialog = sdk.getComponent(
- "views.dialogs.ChatCreateOrReuseDialog",
- );
+ const ChatCreateOrReuseDialog = sdk.getComponent("views.dialogs.ChatCreateOrReuseDialog");
const close = Modal.createTrackedDialog('Create or Reuse', '', ChatCreateOrReuseDialog, {
userId: addrTexts[0],
onNewDMClick: () => {
@@ -115,6 +113,15 @@ function _onStartChatFinished(shouldInvite, addrs) {
});
});
}
+ } else if (addrTexts.length === 1) {
+ // Start a new DM chat
+ createRoom({dmUserId: addrTexts[0]}).catch((err) => {
+ const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
+ Modal.createTrackedDialog('Failed to invite user', '', ErrorDialog, {
+ title: _t("Failed to invite user"),
+ description: ((err && err.message) ? err.message : _t("Operation failed")),
+ });
+ });
} else {
// Start multi user chat
let room;
diff --git a/src/components/structures/MatrixChat.js b/src/components/structures/MatrixChat.js
index a5181b55a7..d6d0b00c84 100644
--- a/src/components/structures/MatrixChat.js
+++ b/src/components/structures/MatrixChat.js
@@ -847,16 +847,36 @@ export default React.createClass({
}).close;
},
+ _leaveRoomWarnings: function(roomId) {
+ const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
+ // Show a warning if there are additional complications.
+ const joinRules = roomToLeave.currentState.getStateEvents('m.room.join_rules', '');
+ const warnings = [];
+ if (joinRules) {
+ const rule = joinRules.getContent().join_rule;
+ if (rule !== "public") {
+ warnings.push((
+
+ { _t("This room is not public. You will not be able to rejoin without an invite.") }
+
+ ));
+ }
+ }
+ return warnings;
+ },
+
_leaveRoom: function(roomId) {
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
-
const roomToLeave = MatrixClientPeg.get().getRoom(roomId);
+ const warnings = this._leaveRoomWarnings(roomId);
+
Modal.createTrackedDialog('Leave room', '', QuestionDialog, {
title: _t("Leave room"),
description: (
{ _t("Are you sure you want to leave the room '%(roomName)s'?", {roomName: roomToLeave.name}) }
+ { warnings }
),
onFinished: (shouldLeave) => {
diff --git a/src/components/views/dialogs/CreateGroupDialog.js b/src/components/views/dialogs/CreateGroupDialog.js
index d811dde09e..86a2b2498c 100644
--- a/src/components/views/dialogs/CreateGroupDialog.js
+++ b/src/components/views/dialogs/CreateGroupDialog.js
@@ -55,11 +55,15 @@ export default React.createClass({
_checkGroupId: function(e) {
let error = null;
- if (!/^[a-z0-9=_\-\.\/]*$/.test(this.state.groupId)) {
+ if (!this.state.groupId) {
+ error = _t("Community IDs cannot not be empty.");
+ } else if (!/^[a-z0-9=_\-\.\/]*$/.test(this.state.groupId)) {
error = _t("Community IDs may only contain characters a-z, 0-9, or '=_-./'");
}
this.setState({
groupIdError: error,
+ // Reset createError to get rid of now stale error message
+ createError: null,
});
return error;
},
diff --git a/src/components/views/elements/Quote.js b/src/components/views/elements/Quote.js
index bceba0f536..761f7aa151 100644
--- a/src/components/views/elements/Quote.js
+++ b/src/components/views/elements/Quote.js
@@ -20,10 +20,11 @@ import PropTypes from 'prop-types';
import MatrixClientPeg from '../../../MatrixClientPeg';
import {wantsDateSeparator} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk';
+import {makeUserPermalink} from "../../../matrix-to";
// For URLs of matrix.to links in the timeline which have been reformatted by
// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
-const REGEX_LOCAL_MATRIXTO = /^#\/room\/(([\#\!])[^\/]*)\/(\$[^\/]*)$/;
+const REGEX_LOCAL_MATRIXTO = /^#\/room\/([\#\!][^\/]*)\/(\$[^\/]*)$/;
export default class Quote extends React.Component {
static isMessageUrl(url) {
@@ -32,111 +33,156 @@ export default class Quote extends React.Component {
static childContextTypes = {
matrixClient: PropTypes.object,
+ addRichQuote: PropTypes.func,
};
static propTypes = {
// The matrix.to url of the event
url: PropTypes.string,
+ // The original node that was rendered
+ node: PropTypes.instanceOf(Element),
// The parent event
parentEv: PropTypes.instanceOf(MatrixEvent),
- // Whether this isn't the first Quote, and we're being nested
- isNested: PropTypes.bool,
};
constructor(props, context) {
super(props, context);
this.state = {
- // The event related to this quote
- event: null,
- show: !this.props.isNested,
+ // The event related to this quote and their nested rich quotes
+ events: [],
+ // Whether the top (oldest) event should be shown or spoilered
+ show: true,
+ // Whether an error was encountered fetching nested older event, show node if it does
+ err: false,
};
this.onQuoteClick = this.onQuoteClick.bind(this);
+ this.addRichQuote = this.addRichQuote.bind(this);
}
getChildContext() {
return {
matrixClient: MatrixClientPeg.get(),
+ addRichQuote: this.addRichQuote,
};
}
+ parseUrl(url) {
+ if (!url) return;
+
+ // Default to the empty array if no match for simplicity
+ // resource and prefix will be undefined instead of throwing
+ const matrixToMatch = REGEX_LOCAL_MATRIXTO.exec(url) || [];
+
+ const [, roomIdentifier, eventId] = matrixToMatch;
+ return {roomIdentifier, eventId};
+ }
+
componentWillReceiveProps(nextProps) {
- let roomId;
- let prefix;
- let eventId;
+ const {roomIdentifier, eventId} = this.parseUrl(nextProps.url);
+ if (!roomIdentifier || !eventId) return;
- if (nextProps.url) {
- // Default to the empty array if no match for simplicity
- // resource and prefix will be undefined instead of throwing
- const matrixToMatch = REGEX_LOCAL_MATRIXTO.exec(nextProps.url) || [];
-
- roomId = matrixToMatch[1]; // The room ID
- prefix = matrixToMatch[2]; // The first character of prefix
- eventId = matrixToMatch[3]; // The event ID
- }
-
- const room = prefix === '#' ?
- MatrixClientPeg.get().getRooms().find((r) => {
- return r.getAliases().includes(roomId);
- }) : MatrixClientPeg.get().getRoom(roomId);
+ const room = this.getRoom(roomIdentifier);
+ if (!room) return;
// Only try and load the event if we know about the room
// otherwise we just leave a `Quote` anchor which can be used to navigate/join the room manually.
- if (room) this.getEvent(room, eventId);
+ this.setState({ events: [] });
+ if (room) this.getEvent(room, eventId, true);
}
componentWillMount() {
this.componentWillReceiveProps(this.props);
}
- async getEvent(room, eventId) {
- let event = room.findEventById(eventId);
+ getRoom(id) {
+ const cli = MatrixClientPeg.get();
+ if (id[0] === '!') return cli.getRoom(id);
+
+ return cli.getRooms().find((r) => {
+ return r.getAliases().includes(id);
+ });
+ }
+
+ async getEvent(room, eventId, show) {
+ const event = room.findEventById(eventId);
if (event) {
- this.setState({room, event});
+ this.addEvent(event, show);
return;
}
await MatrixClientPeg.get().getEventTimeline(room.getUnfilteredTimelineSet(), eventId);
- event = room.findEventById(eventId);
- this.setState({room, event});
+ this.addEvent(room.findEventById(eventId), show);
+ }
+
+ addEvent(event, show) {
+ const events = [event].concat(this.state.events);
+ this.setState({events, show});
+ }
+
+ // addRichQuote(roomId, eventId) {
+ addRichQuote(href) {
+ const {roomIdentifier, eventId} = this.parseUrl(href);
+ if (!roomIdentifier || !eventId) {
+ this.setState({ err: true });
+ return;
+ }
+
+ const room = this.getRoom(roomIdentifier);
+ if (!room) {
+ this.setState({ err: true });
+ return;
+ }
+
+ this.getEvent(room, eventId, false);
}
onQuoteClick() {
- this.setState({
- show: true,
- });
+ this.setState({ show: true });
}
render() {
- const ev = this.state.event;
- if (ev) {
- if (this.state.show) {
- const EventTile = sdk.getComponent('views.rooms.EventTile');
- let dateSep = null;
+ const events = this.state.events.slice();
+ if (events.length) {
+ const evTiles = [];
- const evDate = ev.getDate();
- if (wantsDateSeparator(this.props.parentEv.getDate(), evDate)) {
- const DateSeparator = sdk.getComponent('messages.DateSeparator');
- dateSep =
- { dateSep } -; + evTiles.push(-
+ { + _t('In reply to); } - return', {}, { + 'a': (sub) => { sub }, + 'pill': , + }) + } +
+ { dateSep } +); + }); + + return+
; - - ReactDOM.render(quote, quoteContainer); - node.parentNode.replaceChild(quoteContainer, node); + const quote = +
; + ReactDOM.render(quote, quoteContainer); + node.parentNode.replaceChild(quoteContainer, node); + node = quoteContainer; + } pillified = true; - - node = quoteContainer; } } else if (node.nodeType == Node.TEXT_NODE) { const Pill = sdk.getComponent('elements.Pill'); diff --git a/src/components/views/room_settings/AliasSettings.js b/src/components/views/room_settings/AliasSettings.js index 2915a061de..bd92d75dd9 100644 --- a/src/components/views/room_settings/AliasSettings.js +++ b/src/components/views/room_settings/AliasSettings.js @@ -58,8 +58,8 @@ module.exports = React.createClass({ state.domainToAliases = this.aliasEventsToDictionary(aliasEvents); - state.remoteDomains = Object.keys(state.domainToAliases).filter((alias) => { - return alias !== localDomain; + state.remoteDomains = Object.keys(state.domainToAliases).filter((domain) => { + return domain !== localDomain && state.domainToAliases[domain].length > 0; }); if (canonicalAliasEvent) { diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 937826b4fd..1d8df4c7a6 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -592,7 +592,7 @@ module.exports = withMatrixClient(React.createClass({
\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n\n You can even use 'img' tags\n
\n": "\n Utilitzeu la descripció llarga per a presentar la comunitat a nous membres,\n o per afegir-hi enlaços d'interès. \n
\n\n També podeu utilitzar etiquetes 'img'.\n
\n", + "Add rooms to the community summary": "Afegiu sales al resum de la comunitat", + "Which rooms would you like to add to this summary?": "Quines sales voleu afegir a aquest resum?", + "Add to summary": "Afegeix-ho al resum", + "Failed to add the following rooms to the summary of %(groupId)s:": "No s'ha pogut afegir al resum de la comunitat %(groupId)s les següents sales:", + "Add a Room": "Afegeix una sala", + "Failed to remove the room from the summary of %(groupId)s": "No s'ha pogut eliminar la sala del resum de la comunitat %(groupId)s", + "The room '%(roomName)s' could not be removed from the summary.": "La sala '%(roomName)s' no s'ha pogut eliminar del resum.", + "Add users to the community summary": "Afegeix usuaris al resum de la comunitat", + "Who would you like to add to this summary?": "A qui vol afegir a aquest resum?", + "Failed to add the following users to the summary of %(groupId)s:": "No s'ha pogut afegir al resum de la comunitat %(groupId)s, els següents usuaris:", + "Add a User": "Afegeix un usuari", + "Failed to remove a user from the summary of %(groupId)s": "No s'ha pogut eliminar l'usuari del resum de la comunitat %(groupId)s", + "The user '%(displayName)s' could not be removed from the summary.": "L'usuari '%(displayName)s' no s'ha pogut eliminar del resum.", + "Failed to upload image": "No s'ha pogut pujar la imatge", + "Failed to update community": "No s'ha pogut actualitzar la comunitat", + "Unable to accept invite": "No s'ha pogut acceptar la invitació", + "Unable to reject invite": "No s'ha pogut rebutjar la invitació", + "Leave Community": "Abandona la comunitat", + "Leave %(groupName)s?": "Voleu sortir de la comunitat %(groupName)s?", + "Leave": "Surt", + "Unable to leave room": "No s'ha pogut sortir de la sala", + "Community Settings": "Paràmetres de la comunitat", + "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Aquestes sales es mostren a la pàgina de la comunitat als seus membres i poden entrar-hi fent clic sobre elles.", + "Featured Rooms:": "Sales destacades:", + "Featured Users:": "Usuaris destacats:", + "%(inviter)s has invited you to join this community": "%(inviter)s vos convida a unir-vos a aquesta comunitat", + "You are an administrator of this community": "Sou un administrador d'aquesta comunitat", + "You are a member of this community": "Sou un membre d'aquesta comunitat", + "You are about to be taken to a third-party site so you can authenticate your account for use with %(integrationsUrl)s. Do you wish to continue?": "Esteu a punt de ser portat a un lloc de tercers perquè pugui autenticar-se amb el vostre compte per utilitzar-lo amb %(integrationsUrl)s. Voleu continuar?", + "Your community hasn't got a Long Description, a HTML page to show to community members.\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n\n You can even use 'img' tags\n
\n": "\n Utilice a descrición longa para presentar novos membros a comunidade, ou publicar algunha ligazón importante\n \n
\n\n Tamén pode utilizar etiquetas 'img'\n
\n", + "Add rooms to the community summary": "Engadir salas ao resumo da comunidade", + "Which rooms would you like to add to this summary?": "Qué salas desexa engadir a este resumo?", + "Add to summary": "Engadir ao resumo", + "Failed to add the following rooms to the summary of %(groupId)s:": "Algo fallou ao engadir estas salas ao resumo de %(groupId)s:", + "Add a Room": "Engadir unha sala", + "Failed to remove the room from the summary of %(groupId)s": "Algo fallou ao quitar a sala do resumo de %(groupId)s", + "The room '%(roomName)s' could not be removed from the summary.": "A sala '%(roomName)s' non se puido eliminar do resumo.", + "Add users to the community summary": "Engadir usuarias ao resumo da comunidade", + "Who would you like to add to this summary?": "A quén desexa engadir a este resumo?", + "Failed to add the following users to the summary of %(groupId)s:": "Algo fallou ao engadir as seguintes usuarias ao resumo de %(groupId)s:", + "Add a User": "Engadir unha usuaria", + "Failed to remove a user from the summary of %(groupId)s": "Algo fallou ao eliminar a usuaria do resumo de %(groupId)s", + "The user '%(displayName)s' could not be removed from the summary.": "A usuaria '%(displayName)s' non se puido eliminar do resumo.", + "Failed to upload image": "Fallo ao subir a páxina", + "Failed to update community": "Fallo ao actualizar a comunidade", + "Unable to accept invite": "Non puido aceptar o convite", + "Unable to reject invite": "Non puido rexeitar o convite", + "Leave Community": "Deixar a comunidade", + "Leave %(groupName)s?": "Deixar %(groupName)s?", + "Leave": "Saír", + "Unable to leave room": "Non puido deixar a sala", + "Community Settings": "Axustes da comunidade", + "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "Estas salas son mostradas aos membros da comunidade na páxina da comunidade. Os membros da comunidade poden unirse as salas pulsando en elas.", + "Add rooms to this community": "Engadir salas a esta comunidade", + "Featured Rooms:": "Salas destacadas:", + "Featured Users:": "Usuarias destacadas:", + "%(inviter)s has invited you to join this community": "%(inviter)s convidouna a unirse a esta comunidade", + "You are an administrator of this community": "Vostede administra esta comunidade", + "You are a member of this community": "Vostede é membro de esta comunidade", + "Your community hasn't got a Long Description, a HTML page to show to community members.\n Use the long description to introduce new members to the community, or distribute\n some important links\n
\n\n You can even use 'img' tags\n
\n": "\n 使用長描述來為社群的新成員簡介,或是散發\n 一些重要連結\n
\n\n 您甚至可以使用 'img' 標籤\n
\n", + "Add rooms to the community summary": "新增聊天室到社群摘要中", + "Which rooms would you like to add to this summary?": "您想要新增哪個聊天室到此摘要中?", + "Add to summary": "新增到摘要", + "Failed to add the following rooms to the summary of %(groupId)s:": "新增以下聊天室到 %(groupId)s 的摘要中失敗:", + "Add a Room": "新增聊天室", + "Failed to remove the room from the summary of %(groupId)s": "從 %(groupId)s 的摘要中移除聊天室失敗", + "The room '%(roomName)s' could not be removed from the summary.": "聊天室「%(roomName)s」無法從摘要中移除。", + "Add users to the community summary": "新增使用者到社群摘要中", + "Who would you like to add to this summary?": "您想要新增誰到此摘要中?", + "Failed to add the following users to the summary of %(groupId)s:": "新增下列使用者到 %(groupId)s 的摘要中失敗:", + "Add a User": "新增使用者", + "Failed to remove a user from the summary of %(groupId)s": "從 %(groupId)s 的摘要中移除使用者失敗", + "The user '%(displayName)s' could not be removed from the summary.": "使用者「%(displayName)s」無法從摘要中移除。", + "Failed to update community": "更新社群失敗", + "Unable to accept invite": "無法接受邀請", + "Unable to reject invite": "無法回絕邀請", + "Leave Community": "離開社群", + "Leave %(groupName)s?": "離開 %(groupName)s?", + "Leave": "離開", + "Unable to leave room": "無法離開聊天室", + "Community Settings": "社群設定", + "These rooms are displayed to community members on the community page. Community members can join the rooms by clicking on them.": "這些聊天室在社群頁面上顯示給社群成員。社群成員可以透過點按它們來加入聊天室。", + "%(inviter)s has invited you to join this community": "%(inviter)s 已經邀請您加入此社群", + "You are an administrator of this community": "您是此社群的管理員", + "You are a member of this community": "您是此社群的成員", + "Your community hasn't got a Long Description, a HTML page to show to community members.