2016-03-05 02:30:18 +00:00
/ *
Copyright 2015 , 2016 OpenMarket Ltd
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 .
* /
2017-06-10 13:26:27 +00:00
import MatrixClientPeg from "./MatrixClientPeg" ;
import CallHandler from "./CallHandler" ;
2017-05-25 10:39:08 +00:00
import { _t } from './languageHandler' ;
2017-04-10 09:09:26 +00:00
import * as Roles from './Roles' ;
2017-04-06 16:02:35 +00:00
2015-09-16 13:48:49 +00:00
function textForMemberEvent ( ev ) {
2015-10-30 02:07:04 +00:00
// XXX: SYJS-16 "sender is sometimes null for join messages"
2015-09-16 13:48:49 +00:00
var senderName = ev . sender ? ev . sender . name : ev . getSender ( ) ;
var targetName = ev . target ? ev . target . name : ev . getStateKey ( ) ;
2016-03-05 02:30:18 +00:00
var ConferenceHandler = CallHandler . getConferenceHandler ( ) ;
2015-09-16 13:48:49 +00:00
var reason = ev . getContent ( ) . reason ? (
2017-05-23 14:16:31 +00:00
_t ( 'Reason' ) + ': ' + ev . getContent ( ) . reason
2015-09-16 13:48:49 +00:00
) : "" ;
switch ( ev . getContent ( ) . membership ) {
case 'invite' :
2015-12-17 15:48:14 +00:00
var threePidContent = ev . getContent ( ) . third _party _invite ;
if ( threePidContent ) {
2016-03-02 16:04:24 +00:00
if ( threePidContent . display _name ) {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s accepted the invitation for %(displayName)s.' , { targetName : targetName , displayName : threePidContent . display _name } ) ;
2016-03-02 16:04:24 +00:00
} else {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s accepted an invitation.' , { targetName : targetName } ) ;
2016-03-02 16:04:24 +00:00
}
2015-12-17 15:48:14 +00:00
}
else {
2016-03-05 02:30:18 +00:00
if ( ConferenceHandler && ConferenceHandler . isConferenceUser ( ev . getStateKey ( ) ) ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s requested a VoIP conference.' , { senderName : senderName } ) ;
2016-03-05 02:30:18 +00:00
}
else {
2017-05-25 18:21:18 +00:00
return _t ( '%(senderName)s invited %(targetName)s.' , { senderName : senderName , targetName : targetName } ) ;
2016-03-05 02:30:18 +00:00
}
2015-12-17 15:48:14 +00:00
}
2015-09-16 13:48:49 +00:00
case 'ban' :
2017-05-25 18:21:18 +00:00
return _t (
2017-05-25 18:43:34 +00:00
'%(senderName)s banned %(targetName)s.' ,
{ senderName : senderName , targetName : targetName }
) + ' ' + reason ;
2015-09-16 13:48:49 +00:00
case 'join' :
if ( ev . getPrevContent ( ) && ev . getPrevContent ( ) . membership == 'join' ) {
if ( ev . getPrevContent ( ) . displayname && ev . getContent ( ) . displayname && ev . getPrevContent ( ) . displayname != ev . getContent ( ) . displayname ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s changed their display name from %(oldDisplayName)s to %(displayName)s.' , { senderName : ev . getSender ( ) , oldDisplayName : ev . getPrevContent ( ) . displayname , displayName : ev . getContent ( ) . displayname } ) ;
2015-09-16 13:48:49 +00:00
} else if ( ! ev . getPrevContent ( ) . displayname && ev . getContent ( ) . displayname ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s set their display name to %(displayName)s.' , { senderName : ev . getSender ( ) , displayName : ev . getContent ( ) . displayname } ) ;
2015-09-16 13:48:49 +00:00
} else if ( ev . getPrevContent ( ) . displayname && ! ev . getContent ( ) . displayname ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s removed their display name (%(oldDisplayName)s).' , { senderName : ev . getSender ( ) , oldDisplayName : ev . getPrevContent ( ) . displayname } ) ;
2015-09-16 13:48:49 +00:00
} else if ( ev . getPrevContent ( ) . avatar _url && ! ev . getContent ( ) . avatar _url ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s removed their profile picture.' , { senderName : senderName } ) ;
2015-09-16 13:48:49 +00:00
} else if ( ev . getPrevContent ( ) . avatar _url && ev . getContent ( ) . avatar _url && ev . getPrevContent ( ) . avatar _url != ev . getContent ( ) . avatar _url ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s changed their profile picture.' , { senderName : senderName } ) ;
2015-09-16 13:48:49 +00:00
} else if ( ! ev . getPrevContent ( ) . avatar _url && ev . getContent ( ) . avatar _url ) {
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s set a profile picture.' , { senderName : senderName } ) ;
2016-09-02 15:54:27 +00:00
} else {
2017-05-06 00:45:28 +00:00
// suppress null rejoins
return '' ;
2015-09-16 13:48:49 +00:00
}
} else {
if ( ! ev . target ) console . warn ( "Join message has no target! -- " + ev . getContent ( ) . state _key ) ;
2016-03-05 02:30:18 +00:00
if ( ConferenceHandler && ConferenceHandler . isConferenceUser ( ev . getStateKey ( ) ) ) {
2017-05-27 13:55:55 +00:00
return _t ( 'VoIP conference started.' ) ;
2016-03-05 02:30:18 +00:00
}
else {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s joined the room.' , { targetName : targetName } ) ;
2016-03-05 02:30:18 +00:00
}
2015-09-16 13:48:49 +00:00
}
case 'leave' :
if ( ev . getSender ( ) === ev . getStateKey ( ) ) {
2016-03-05 02:30:18 +00:00
if ( ConferenceHandler && ConferenceHandler . isConferenceUser ( ev . getStateKey ( ) ) ) {
2017-05-27 13:55:55 +00:00
return _t ( 'VoIP conference finished.' ) ;
2016-03-05 02:30:18 +00:00
}
2016-03-16 01:16:15 +00:00
else if ( ev . getPrevContent ( ) . membership === "invite" ) {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s rejected the invitation.' , { targetName : targetName } ) ;
2016-03-16 01:16:15 +00:00
}
2016-03-05 02:30:18 +00:00
else {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s left the room.' , { targetName : targetName } ) ;
2016-03-05 02:30:18 +00:00
}
2015-09-16 13:48:49 +00:00
}
else if ( ev . getPrevContent ( ) . membership === "ban" ) {
2017-05-25 18:43:34 +00:00
return _t ( '%(senderName)s unbanned %(targetName)s.' , { senderName : senderName , targetName : targetName } ) ;
2015-09-16 13:48:49 +00:00
}
else if ( ev . getPrevContent ( ) . membership === "join" ) {
2017-05-25 18:21:18 +00:00
return _t (
2017-05-25 18:25:06 +00:00
'%(senderName)s kicked %(targetName)s.' ,
{ senderName : senderName , targetName : targetName }
2017-05-25 18:43:34 +00:00
) + ' ' + reason ;
2015-09-16 13:48:49 +00:00
}
2015-10-05 15:44:50 +00:00
else if ( ev . getPrevContent ( ) . membership === "invite" ) {
2017-05-25 18:21:18 +00:00
return _t (
2017-05-27 13:57:52 +00:00
'%(senderName)s withdrew %(targetName)s\'s invitation.' ,
2017-05-25 18:25:06 +00:00
{ senderName : senderName , targetName : targetName }
2017-05-25 18:43:34 +00:00
) + ' ' + reason ;
2015-10-05 15:44:50 +00:00
}
2015-09-16 13:48:49 +00:00
else {
2017-05-25 18:21:18 +00:00
return _t ( '%(targetName)s left the room.' , { targetName : targetName } ) ;
2015-09-16 13:48:49 +00:00
}
}
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
function textForTopicEvent ( ev ) {
var senderDisplayName = ev . sender && ev . sender . name ? ev . sender . name : ev . getSender ( ) ;
2017-05-27 13:55:55 +00:00
return _t ( '%(senderDisplayName)s changed the topic to "%(topic)s".' , { senderDisplayName : senderDisplayName , topic : ev . getContent ( ) . topic } ) ;
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
2015-10-30 02:07:04 +00:00
function textForRoomNameEvent ( ev ) {
var senderDisplayName = ev . sender && ev . sender . name ? ev . sender . name : ev . getSender ( ) ;
2017-06-10 13:26:27 +00:00
2017-05-30 05:21:14 +00:00
if ( ! ev . getContent ( ) . name || ev . getContent ( ) . name . trim ( ) . length === 0 ) {
return _t ( '%(senderDisplayName)s removed the room name.' , { senderDisplayName : senderDisplayName } ) ;
}
2017-05-27 13:55:55 +00:00
return _t ( '%(senderDisplayName)s changed the room name to %(roomName)s.' , { senderDisplayName : senderDisplayName , roomName : ev . getContent ( ) . name } ) ;
2016-09-15 16:01:02 +00:00
}
2015-10-30 02:07:04 +00:00
2015-09-16 13:48:49 +00:00
function textForMessageEvent ( ev ) {
var senderDisplayName = ev . sender && ev . sender . name ? ev . sender . name : ev . getSender ( ) ;
var message = senderDisplayName + ': ' + ev . getContent ( ) . body ;
if ( ev . getContent ( ) . msgtype === "m.emote" ) {
message = "* " + senderDisplayName + " " + message ;
} else if ( ev . getContent ( ) . msgtype === "m.image" ) {
2017-05-25 18:21:18 +00:00
message = _t ( '%(senderDisplayName)s sent an image.' , { senderDisplayName : senderDisplayName } ) ;
2015-09-16 13:48:49 +00:00
}
return message ;
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
2017-09-14 12:43:46 +00:00
function textForRoomAliasesEvent ( ev ) {
const senderName = event . sender ? event . sender . name : event . getSender ( ) ;
const oldAliases = ev . getPrevContent ( ) . aliases || [ ] ;
const newAliases = ev . getContent ( ) . aliases || [ ] ;
const addedAliases = newAliases . filter ( ( x ) => ! oldAliases . includes ( x ) ) ;
const removedAliases = oldAliases . filter ( ( x ) => ! newAliases . includes ( x ) ) ;
if ( ! addedAliases . length && ! removedAliases . length ) {
return '' ;
}
if ( addedAliases . length && ! removedAliases . length ) {
return _t ( '%(senderName)s added %(addedAddresses)s as addresses for this room.' , {
senderName : senderName ,
addedAddresses : addedAliases . join ( ', ' ) ,
} ) ;
} else if ( ! addedAliases . length && removedAliases . length ) {
return _t ( '%(senderName)s removed %(addresses)s as addresses for this room.' , {
senderName : senderName ,
removedAddresses : removedAliases . join ( ', ' ) ,
} ) ;
} else {
return _t ( '%(senderName)s added %(addedAddresses)s and removed %(removedAddresses)s as addresses for this room.' , {
senderName : senderName ,
addedAddresses : addedAliases . join ( ', ' ) ,
removedAddresses : removedAliases . join ( ', ' ) ,
} ) ;
}
}
2015-09-16 13:48:49 +00:00
function textForCallAnswerEvent ( event ) {
2017-05-23 14:16:31 +00:00
var senderName = event . sender ? event . sender . name : _t ( 'Someone' ) ;
var supported = MatrixClientPeg . get ( ) . supportsVoip ( ) ? "" : _t ( '(not supported by this browser)' ) ;
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s answered the call.' , { senderName : senderName } ) + ' ' + supported ;
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
function textForCallHangupEvent ( event ) {
2017-06-10 13:26:27 +00:00
const senderName = event . sender ? event . sender . name : _t ( 'Someone' ) ;
const eventContent = event . getContent ( ) ;
let reason = "" ;
if ( ! MatrixClientPeg . get ( ) . supportsVoip ( ) ) {
2017-06-11 06:19:19 +00:00
reason = _t ( '(not supported by this browser)' ) ;
2017-06-10 13:26:27 +00:00
} else if ( eventContent . reason ) {
2017-06-11 06:19:19 +00:00
if ( eventContent . reason === "ice_failed" ) {
reason = _t ( '(could not connect media)' ) ;
} else if ( eventContent . reason === "invite_timeout" ) {
reason = _t ( '(no answer)' ) ;
} else {
reason = _t ( '(unknown failure: %(reason)s)' , { reason : eventContent . reason } ) ;
}
2017-06-10 13:26:27 +00:00
}
2017-06-10 13:35:11 +00:00
return _t ( '%(senderName)s ended the call.' , { senderName } ) + ' ' + reason ;
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
function textForCallInviteEvent ( event ) {
2017-05-23 14:16:31 +00:00
var senderName = event . sender ? event . sender . name : _t ( 'Someone' ) ;
2015-09-16 13:48:49 +00:00
// FIXME: Find a better way to determine this from the event?
var type = "voice" ;
if ( event . getContent ( ) . offer && event . getContent ( ) . offer . sdp &&
event . getContent ( ) . offer . sdp . indexOf ( 'm=video' ) !== - 1 ) {
type = "video" ;
}
2017-05-23 14:16:31 +00:00
var supported = MatrixClientPeg . get ( ) . supportsVoip ( ) ? "" : _t ( '(not supported by this browser)' ) ;
2017-05-25 18:43:34 +00:00
return _t ( '%(senderName)s placed a %(callType)s call.' , { senderName : senderName , callType : type } ) + ' ' + supported ;
2016-09-15 16:01:02 +00:00
}
2015-09-16 13:48:49 +00:00
2015-12-17 15:48:14 +00:00
function textForThreePidInviteEvent ( event ) {
var senderName = event . sender ? event . sender . name : event . getSender ( ) ;
2017-05-25 18:21:18 +00:00
return _t ( '%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.' , { senderName : senderName , targetDisplayName : event . getContent ( ) . display _name } ) ;
2016-09-15 16:01:02 +00:00
}
2015-12-17 15:48:14 +00:00
2016-03-15 23:47:40 +00:00
function textForHistoryVisibilityEvent ( event ) {
var senderName = event . sender ? event . sender . name : event . getSender ( ) ;
var vis = event . getContent ( ) . history _visibility ;
2017-05-25 18:21:18 +00:00
// XXX: This i18n just isn't going to work for languages with different sentence structure.
2017-05-23 14:16:31 +00:00
var text = _t ( '%(senderName)s made future room history visible to' , { senderName : senderName } ) + ' ' ;
2016-03-15 23:47:40 +00:00
if ( vis === "invited" ) {
2017-05-23 14:16:31 +00:00
text += _t ( 'all room members, from the point they are invited' ) + '.' ;
2016-03-15 23:47:40 +00:00
}
else if ( vis === "joined" ) {
2017-05-23 14:16:31 +00:00
text += _t ( 'all room members, from the point they joined' ) + '.' ;
2016-03-15 23:47:40 +00:00
}
else if ( vis === "shared" ) {
2017-05-23 14:16:31 +00:00
text += _t ( 'all room members' ) + '.' ;
2016-03-15 23:47:40 +00:00
}
else if ( vis === "world_readable" ) {
2017-05-23 14:16:31 +00:00
text += _t ( 'anyone' ) + '.' ;
2016-03-15 23:47:40 +00:00
}
else {
2017-05-27 16:36:02 +00:00
text += ' ' + _t ( 'unknown' ) + ' (' + vis + ').' ;
2016-03-15 23:47:40 +00:00
}
return text ;
2016-09-15 16:01:02 +00:00
}
function textForEncryptionEvent ( event ) {
var senderName = event . sender ? event . sender . name : event . getSender ( ) ;
2017-05-27 13:55:55 +00:00
return _t ( '%(senderName)s turned on end-to-end encryption (algorithm %(algorithm)s).' , { senderName : senderName , algorithm : event . getContent ( ) . algorithm } ) ;
2016-09-15 16:01:02 +00:00
}
2016-03-15 23:47:40 +00:00
2017-04-06 16:02:35 +00:00
// Currently will only display a change if a user's power level is changed
function textForPowerEvent ( event ) {
const senderName = event . sender ? event . sender . name : event . getSender ( ) ;
if ( ! event . getPrevContent ( ) || ! event . getPrevContent ( ) . users ) {
return '' ;
}
const userDefault = event . getContent ( ) . users _default || 0 ;
// Construct set of userIds
let users = [ ] ;
Object . keys ( event . getContent ( ) . users ) . forEach (
( userId ) => {
if ( users . indexOf ( userId ) === - 1 ) users . push ( userId ) ;
}
) ;
Object . keys ( event . getPrevContent ( ) . users ) . forEach (
( userId ) => {
if ( users . indexOf ( userId ) === - 1 ) users . push ( userId ) ;
}
) ;
let diff = [ ] ;
2017-05-25 18:21:18 +00:00
// XXX: This is also surely broken for i18n
2017-04-06 16:02:35 +00:00
users . forEach ( ( userId ) => {
// Previous power level
const from = event . getPrevContent ( ) . users [ userId ] ;
// Current power level
const to = event . getContent ( ) . users [ userId ] ;
if ( to !== from ) {
diff . push (
2017-05-27 14:52:24 +00:00
_t ( '%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s' , {
userId : userId ,
fromPowerLevel : Roles . textualPowerLevel ( from , userDefault ) ,
toPowerLevel : Roles . textualPowerLevel ( to , userDefault )
} )
2017-04-06 16:02:35 +00:00
) ;
}
} ) ;
if ( ! diff . length ) {
return '' ;
}
2017-05-27 14:52:24 +00:00
return _t ( '%(senderName)s changed the power level of %(powerLevelDiffText)s.' , {
senderName : senderName ,
2017-08-28 07:19:39 +00:00
powerLevelDiffText : diff . join ( ", " ) ,
2017-05-27 14:52:24 +00:00
} ) ;
2017-04-06 16:02:35 +00:00
}
2017-08-16 16:46:20 +00:00
function textForWidgetEvent ( event ) {
2017-08-28 07:19:39 +00:00
const senderName = event . getSender ( ) ;
const { name : prevName , type : prevType , url : prevUrl } = event . getPrevContent ( ) ;
2017-08-18 11:03:29 +00:00
const { name , type , url } = event . getContent ( ) || { } ;
2017-08-28 07:19:39 +00:00
let widgetName = name || prevName || type || prevType || '' ;
2017-08-18 17:02:50 +00:00
// Apply sentence case to widget name
if ( widgetName && widgetName . length > 0 ) {
widgetName = widgetName [ 0 ] . toUpperCase ( ) + widgetName . slice ( 1 ) + ' ' ;
}
2017-08-16 16:46:20 +00:00
2017-08-18 11:04:34 +00:00
// If the widget was removed, its content should be {}, but this is sufficiently
// equivalent to that condition.
2017-08-18 09:45:43 +00:00
if ( url ) {
2017-08-28 07:19:39 +00:00
if ( prevUrl ) {
return _t ( '%(widgetName)s widget modified by %(senderName)s' , {
widgetName , senderName ,
} ) ;
} else {
return _t ( '%(widgetName)s widget added by %(senderName)s' , {
widgetName , senderName ,
} ) ;
}
2017-08-16 16:46:20 +00:00
} else {
2017-08-18 17:02:50 +00:00
return _t ( '%(widgetName)s widget removed by %(senderName)s' , {
widgetName , senderName ,
2017-08-16 16:46:20 +00:00
} ) ;
}
}
2015-09-16 13:48:49 +00:00
var handlers = {
'm.room.message' : textForMessageEvent ,
2017-09-14 12:43:46 +00:00
'm.room.aliases' : textForRoomAliasesEvent ,
2015-10-30 02:07:04 +00:00
'm.room.name' : textForRoomNameEvent ,
'm.room.topic' : textForTopicEvent ,
'm.room.member' : textForMemberEvent ,
'm.call.invite' : textForCallInviteEvent ,
'm.call.answer' : textForCallAnswerEvent ,
'm.call.hangup' : textForCallHangupEvent ,
2016-03-15 23:47:40 +00:00
'm.room.third_party_invite' : textForThreePidInviteEvent ,
'm.room.history_visibility' : textForHistoryVisibilityEvent ,
2016-09-15 16:01:02 +00:00
'm.room.encryption' : textForEncryptionEvent ,
2017-04-06 16:02:35 +00:00
'm.room.power_levels' : textForPowerEvent ,
2017-08-16 16:46:20 +00:00
'im.vector.modular.widgets' : textForWidgetEvent ,
2015-09-16 13:48:49 +00:00
} ;
module . exports = {
textForEvent : function ( ev ) {
var hdlr = handlers [ ev . getType ( ) ] ;
if ( ! hdlr ) return "" ;
return hdlr ( ev ) ;
}
2017-01-20 14:22:27 +00:00
} ;