Skip to content

Commit

Permalink
Merge pull request #404 from threema-ch/mention
Browse files Browse the repository at this point in the history
Implement mention
  • Loading branch information
dbrgn authored Jan 26, 2018
2 parents 364ccf4 + e276717 commit 56efcaf
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 6 deletions.
3 changes: 2 additions & 1 deletion public/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@
"THREEMA_BLOCKED_RECEIVER": "blockiert",
"DELETE_THREAD": "Chat löschen",
"DELETE_THREAD_MESSAGE": "{count, plural, one {Möchten Sie wirklich diesen Chat löschen? Die Nachrichten können nicht wiederhergestellt werden.} other {Möchten Sie wirklich # Chats löschen? Die Nachrichten können nicht wiederhergestellt werden.}}",
"MUTED": "Keine Benachrichtigungen"
"MUTED": "Keine Benachrichtigungen",
"ALL": "Alle"
},
"messageTypes": {
"AUDIO_MESSAGE": "Sprachnachricht",
Expand Down
3 changes: 2 additions & 1 deletion public/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@
"THREEMA_BLOCKED_RECEIVER": "blocked",
"DELETE_THREAD": "Delete chat",
"DELETE_THREAD_MESSAGE": "{count, plural, one {Do you really want to delete this chat? You will not be able to recover the messages.} other {Do you really want to delete # chat(s)? You will not be able to recover the messages.}}",
"MUTED": "No notifications"
"MUTED": "No notifications",
"ALL": "All"
},
"messageTypes": {
"AUDIO_MESSAGE": "Audio Message",
Expand Down
2 changes: 1 addition & 1 deletion src/directives/message_quote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export default [
<div class="message-quote-content" ng-style="{'border-color': ctrl.contact().color}">
<span class="message-name" ng-style="{'color': ctrl.contact().color}"
ng-bind-html="ctrl.contact().displayName | emojify"></span>
<span class="message-quote" ng-bind-html="ctrl.quote.text | escapeHtml | markify | emojify | linkify | nlToBr"></span>
<span class="message-quote" ng-bind-html="ctrl.quote.text | escapeHtml | markify | emojify | linkify | mentionify | nlToBr"></span>
</div>
`,
};
Expand Down
2 changes: 1 addition & 1 deletion src/directives/message_text.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export default [
}
}],
template: `
<span threema-action ng-bind-html="ctrl.text | escapeHtml | markify | emojify | linkify | nlToBr: ctrl.multiLine"></span>
<span threema-action ng-bind-html="ctrl.text | escapeHtml | markify | emojify | mentionify | linkify | nlToBr: ctrl.multiLine"></span>
`,
};
},
Expand Down
38 changes: 37 additions & 1 deletion src/filters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* along with Threema Web. If not, see <http://www.gnu.org/licenses/>.
*/

import {filter} from './helpers';
import {escapeRegExp, filter} from './helpers';
import {MimeService} from './services/mime';
import {WebClientService} from './services/webclient';

Expand Down Expand Up @@ -126,6 +126,42 @@ angular.module('3ema.filters', [])
};
})

/**
* Convert mention elements to html elements
*/
.filter('mentionify', ['WebClientService', '$translate',
function (webClientService: WebClientService, $translate) {
return(text) => {
if (text !== null && text.length > 10) {
let result = text.match(/@\[([\*\@a-zA-Z0-9][\@a-zA-Z0-9]{7})\]/g);
if (result !== null) {
result = ([...new Set(result)]);
// Unique
for (let possibleMention of result) {
let identity = possibleMention.substr(2, 8);
let html;

if (identity === '@@@@@@@@') {
html = '<span class="mention all">' + $translate.instant('messenger.ALL') + '</span>';
} else {
const contact = webClientService.contacts.get(possibleMention.substr(2, 8));
if (contact !== null) {
html = '<span class="mention contact">' + contact.displayName + '</span>';
}
}

if (html !== undefined) {
text = text.replace(
new RegExp(escapeRegExp(possibleMention), 'g'),
html,
);
}
}
}
}
return text;
};
}])
/**
* Reverse an array.
*/
Expand Down
9 changes: 9 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,3 +242,12 @@ export function supportsPassive() {
window.addEventListener('test', null, opts);
} catch (e) { /* do nothing */ }
}

/**
* Excape a RegEx, so that none of the string characters are considered special characters.
*
* Taken from https://stackoverflow.com/a/17606289/284318
*/
export function escapeRegExp(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
1 change: 1 addition & 0 deletions src/sass/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
@import "components/drag_file";
@import "components/buttons";
@import "components/mediabox";
@import "components/mention";
@import "components/backbutton";
@import "components/emoji";
@import "components/battery";
Expand Down
10 changes: 10 additions & 0 deletions src/sass/components/_mention.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.mention {
background-color: #E0E0E0;
padding: 2px 5px;
position: relative;
-webkit-border-radius:5px;
-moz-border-radius:5px;
border-radius:5px;
box-shadow: 0 1px 1px rgba(0,0,0,0.1);
color: black;
}
80 changes: 79 additions & 1 deletion tests/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,49 @@ describe('Filters', function() {

// Ignoring page reload request
beforeAll(() => window.onbeforeunload = () => null);
let webClientServiceMock = {
contacts: {
get: function(id) {
if (id === 'AAAAAAAA') {
return {
displayName: 'ContactA'
}
}
else if (id === 'XXXXXXXX') {
return {
displayName: 'ContactX'
}
}
else if (id === '*AAAAAAA') {
return {
displayName: 'GWContactA'
}
}
return null;
}
}
};

let translationMock = {
instant: function(label) {
return label;
}
};

beforeEach(function() {

// Load 3ema.filters module
module('3ema.filters');

module(function ($provide) {
$provide.value('WebClientService', webClientServiceMock);
$provide.value('$translate', translationMock);
});

// Inject the $filter function
inject(function(_$filter_) {
$filter = _$filter_;
});

});

function testPatterns(filterName, cases) {
Expand Down Expand Up @@ -121,4 +153,50 @@ describe('Filters', function() {

});

describe('mentionify', function() {


this.testPatterns = (cases) => testPatterns('mentionify', cases);

it('no mentions', () => {
this.testPatterns([
['', ''],
['hello my friend', 'hello my friend'],
['@[AAAAAAA]', '@[AAAAAAA]'],
['this is not a valid @[AAAAAAA]', 'this is not a valid @[AAAAAAA]'],
['@[@@@@@@@]', '@[@@@@@@@]'],
['this is not a valid @[@@@@@@@]', 'this is not a valid @[@@@@@@@]'],
]);
});

it('mention - no contacts', () => {
this.testPatterns([
['@[BBBBBBBB]', '@[BBBBBBBB]'],
['@[*BBBBBBB]', '@[*BBBBBBB]'],
]);
});

it('mention - contact', () => {
this.testPatterns([
['@[AAAAAAAA]', '<span class="mention contact">ContactA</span>'],
['hello @[AAAAAAAA]. @[AAAAAAAA] you are my friend', 'hello <span class="mention contact">ContactA</span>. <span class="mention contact">ContactA</span> you are my friend'],
['@[AAAAAAAA] @[AAAAAAAA] @[AAAAAAAA]', '<span class="mention contact">ContactA</span> <span class="mention contact">ContactA</span> <span class="mention contact">ContactA</span>']
]);
});

it('mention - all', () => {
this.testPatterns([
['@[@@@@@@@@]', '<span class="mention all">messenger.ALL</span>'],
['@[@@@@@@@@] your base are belong to us', '<span class="mention all">messenger.ALL</span> your base are belong to us'],
['@[@@@@@@@@] @[@@@@@@@@] @[@@@@@@@@]', '<span class="mention all">messenger.ALL</span> <span class="mention all">messenger.ALL</span> <span class="mention all">messenger.ALL</span>']
]);
});

it('mention - mixed', () => {
this.testPatterns([
['@[@@@@@@@@] @[AAAAAAAA] @[BBBBBBBB]', '<span class="mention all">messenger.ALL</span> <span class="mention contact">ContactA</span> @[BBBBBBBB]'],
]);
});
});

});

0 comments on commit 56efcaf

Please sign in to comment.