diff --git a/Gruntfile.js b/Gruntfile.js index 3966d5852..ed8d7d848 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -180,7 +180,12 @@ module.exports = grunt => { tasks: ['sass'], }, transpile: { - files: ['./ts/**/*.ts', './ts/**/*.tsx', './ts/**/**/*.tsx'], + files: [ + './ts/**/*.ts', + './ts/**/*.tsx', + './ts/**/**/*.tsx', + './test/ts/**.ts', + ], tasks: ['exec:transpile'], }, }, diff --git a/js/ConversationController.d.ts b/js/ConversationController.d.ts index cae0e0179..4e5032a52 100644 --- a/js/ConversationController.d.ts +++ b/js/ConversationController.d.ts @@ -7,4 +7,5 @@ export type ConversationControllerType = { getOrThrow: (id: string) => ConversationModel; getOrCreateAndWait: (id: string, type: string) => Promise; getOrCreate: (id: string, type: string) => Promise; + dangerouslyCreateAndAdd: (any) => any; }; diff --git a/password.html b/password.html index a399efe5b..594a0d773 100644 --- a/password.html +++ b/password.html @@ -26,8 +26,8 @@
diff --git a/test/backup_test.js b/test/backup_test.js index 78b18360e..1395b949d 100644 --- a/test/backup_test.js +++ b/test/backup_test.js @@ -487,8 +487,10 @@ describe('Backup', () => { console.log('Backup test: Create models, save to db/disk'); const message = await upgradeMessageSchema(messageWithAttachments); - console.log({ message }); - await message.commit(); + await window.Signal.Data.saveMessage(message, { + Message: Whisper.Message, + forceSave: true, + }); const conversation = { active_at: 1524185933350, diff --git a/test/fixtures.js b/test/fixtures.js index ed36bac81..8aef0aa32 100644 --- a/test/fixtures.js +++ b/test/fixtures.js @@ -1,11 +1,11 @@ /* global Whisper */ Whisper.Fixtures = () => { - const VERA_ID = '+13016886524'; // nsa - const NESTOR_ID = '+17034820623'; // cia - const MASHA_ID = '+441242221491'; // gchq - const FRED_ID = '+14155537400'; // fbi sf - const MICHEL_ID = '+12024561111'; // twh + const VERA_ID = '0501cd123456789abcdef05123456789abcdef05123456789abcdef05123456789'; // nsa + const NESTOR_ID = '0502cd123456789abcdef05123456789abcdef05123456789abcdef05123456789'; // cia + const MASHA_ID = '0503cd123456789abcdef05123456789abcdef05123456789abcdef05123456789'; // gchq + const FRED_ID = '0504cd123456789abcdef05123456789abcdef05123456789abcdef05123456789'; // fbi sf + const MICHEL_ID = '0505cd123456789abcdef05123456789abcdef05123456789abcdef05123456789'; // twh const now = Date.now(); const conversationCollection = new Whisper.ConversationCollection([ @@ -172,7 +172,7 @@ Whisper.Fixtures = () => { type: 'group', active_at: now - 100000, timestamp: now - 100000, - id: 'group1', + id: '05abcd123456789abcdef05123456789abcdef05123456789abcdef05123456789', lastMessage: 'See you all there!', members: [MICHEL_ID, FRED_ID, NESTOR_ID], }); diff --git a/test/fixtures_test.js b/test/fixtures_test.js index e4e6ac8d2..a100fed1b 100644 --- a/test/fixtures_test.js +++ b/test/fixtures_test.js @@ -9,7 +9,7 @@ describe('Fixtures', () => { await clearDatabase(); await textsecure.storage.user.setNumberAndDeviceId( - '+17015552000', + '05123456789abcdef05123456789abcdef05123456789abcdef05123456789abcd', 2, 'testDevice' ); @@ -27,12 +27,10 @@ describe('Fixtures', () => { await ConversationController.load(); let view = new Whisper.InboxView({ window }); - view.onEmpty(); view.$el.prependTo($('#render-light-theme')); view = new Whisper.InboxView({ window }); view.$el.removeClass('light-theme').addClass('dark-theme'); - view.onEmpty(); view.$el.prependTo($('#render-dark-theme')); }); }); diff --git a/test/index.html b/test/index.html index eb6196a23..36207ad28 100644 --- a/test/index.html +++ b/test/index.html @@ -19,8 +19,8 @@ diff --git a/test/keychange_listener_test.js b/test/keychange_listener_test.js deleted file mode 100644 index 43a8c759b..000000000 --- a/test/keychange_listener_test.js +++ /dev/null @@ -1,79 +0,0 @@ -/* global ConversationController, libsignal, SignalProtocolStore, Whisper */ - -describe('KeyChangeListener', () => { - const phoneNumberWithKeyChange = '+13016886524'; // nsa - const address = new libsignal.SignalProtocolAddress( - phoneNumberWithKeyChange, - 1 - ); - const oldKey = libsignal.crypto.getRandomBytes(33); - const newKey = libsignal.crypto.getRandomBytes(33); - let store; - - beforeEach(async () => { - store = new SignalProtocolStore(); - await store.hydrateCaches(); - Whisper.KeyChangeListener.init(store); - return store.saveIdentity(address.toString(), oldKey); - }); - - afterEach(() => { - return store.removeIdentityKey(phoneNumberWithKeyChange); - }); - - describe('When we have a conversation with this contact', () => { - let convo; - before(async () => { - convo = ConversationController.dangerouslyCreateAndAdd({ - id: phoneNumberWithKeyChange, - type: 'private', - }); - await window.Signal.Data.saveConversation(convo.attributes, { - Conversation: Whisper.Conversation, - }); - }); - - after(async () => { - await convo.destroyMessages(); - await window.Signal.Data.saveConversation(convo.id); - }); - - it('generates a key change notice in the private conversation with this contact', done => { - convo.once('newmessage', async () => { - await convo.fetchMessages(); - const message = convo.messageCollection.at(0); - assert.strictEqual(message.get('type'), 'keychange'); - done(); - }); - store.saveIdentity(address.toString(), newKey); - }); - }); - - describe('When we have a group with this contact', () => { - let convo; - before(async () => { - convo = ConversationController.dangerouslyCreateAndAdd({ - id: 'groupId', - type: 'group', - members: [phoneNumberWithKeyChange], - }); - await window.Signal.Data.saveConversation(convo.attributes, { - Conversation: Whisper.Conversation, - }); - }); - after(async () => { - await convo.destroyMessages(); - await window.Signal.Data.saveConversation(convo.id); - }); - - it('generates a key change notice in the group conversation with this contact', done => { - convo.once('newmessage', async () => { - await convo.fetchMessages(); - const message = convo.messageCollection.at(0); - assert.strictEqual(message.get('type'), 'keychange'); - done(); - }); - store.saveIdentity(address.toString(), newKey); - }); - }); -}); diff --git a/test/modules/link_previews_test.js b/test/modules/link_previews_test.js deleted file mode 100644 index 0a5800982..000000000 --- a/test/modules/link_previews_test.js +++ /dev/null @@ -1,326 +0,0 @@ -const { assert } = require('chai'); - -const { - findLinks, - getTitleMetaTag, - getImageMetaTag, - isLinkInWhitelist, - isLinkSneaky, -} = require('../../js/modules/link_previews'); - -describe('Link previews', () => { - describe('#isLinkInWhitelist', () => { - it('returns true for valid links', () => { - assert.strictEqual(isLinkInWhitelist('https://youtube.com/blah'), true); - assert.strictEqual( - isLinkInWhitelist('https://www.youtube.com/blah'), - true - ); - assert.strictEqual(isLinkInWhitelist('https://m.youtube.com/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://youtu.be/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://reddit.com/blah'), true); - assert.strictEqual( - isLinkInWhitelist('https://www.reddit.com/blah'), - true - ); - assert.strictEqual(isLinkInWhitelist('https://m.reddit.com/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://imgur.com/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://www.imgur.com/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://m.imgur.com/blah'), true); - assert.strictEqual(isLinkInWhitelist('https://instagram.com/blah'), true); - assert.strictEqual( - isLinkInWhitelist('https://www.instagram.com/blah'), - true - ); - assert.strictEqual( - isLinkInWhitelist('https://m.instagram.com/blah'), - true - ); - }); - - it('returns false for subdomains', () => { - assert.strictEqual( - isLinkInWhitelist('https://any.subdomain.youtube.com/blah'), - false - ); - assert.strictEqual( - isLinkInWhitelist('https://any.subdomain.instagram.com/blah'), - false - ); - }); - - it('returns false for http links', () => { - assert.strictEqual(isLinkInWhitelist('http://instagram.com/blah'), false); - assert.strictEqual(isLinkInWhitelist('http://youtube.com/blah'), false); - }); - - it('returns false for links with no protocol', () => { - assert.strictEqual(isLinkInWhitelist('instagram.com/blah'), false); - assert.strictEqual(isLinkInWhitelist('youtube.com/blah'), false); - }); - - it('returns false for link to root path', () => { - assert.strictEqual(isLinkInWhitelist('https://instagram.com'), false); - assert.strictEqual(isLinkInWhitelist('https://youtube.com'), false); - - assert.strictEqual(isLinkInWhitelist('https://instagram.com/'), false); - assert.strictEqual(isLinkInWhitelist('https://youtube.com/'), false); - }); - - it('returns false for other well-known sites', () => { - assert.strictEqual(isLinkInWhitelist('https://facebook.com/blah'), false); - assert.strictEqual(isLinkInWhitelist('https://twitter.com/blah'), false); - }); - - it('returns false for links that look like our target links', () => { - assert.strictEqual( - isLinkInWhitelist('https://evil.site.com/.instagram.com/blah'), - false - ); - assert.strictEqual( - isLinkInWhitelist('https://evil.site.com/.instagram.com/blah'), - false - ); - assert.strictEqual( - isLinkInWhitelist('https://sinstagram.com/blah'), - false - ); - }); - }); - - describe('#_getMetaTag', () => { - it('returns html-decoded tag contents from Youtube', () => { - const youtube = ` - - - - - - `; - - assert.strictEqual( - 'Randomness is Random - Numberphile', - getTitleMetaTag(youtube) - ); - assert.strictEqual( - 'https://i.ytimg.com/vi/tP-Ipsat90c/maxresdefault.jpg', - getImageMetaTag(youtube) - ); - }); - - it('returns html-decoded tag contents from Instagram', () => { - const instagram = ` - - - - - - - `; - - assert.strictEqual( - 'Walter "MFPallytime" on Instagram: “Lol gg”', - getTitleMetaTag(instagram) - ); - assert.strictEqual( - 'https://scontent-lax3-1.cdninstagram.com/vp/1c69aa381c2201720c29a6c28de42ffd/5CD49B5B/t51.2885-15/e35/47690175_2275988962411653_1145978227188801192_n.jpg?_nc_ht=scontent-lax3-1.cdninstagram.com', - getImageMetaTag(instagram) - ); - }); - - it('returns html-decoded tag contents from Imgur', () => { - const imgur = ` - - - - - - - - - `; - - assert.strictEqual('', getTitleMetaTag(imgur)); - assert.strictEqual( - 'https://i.imgur.com/Y3wjlwY.jpg?fb', - getImageMetaTag(imgur) - ); - }); - - it('returns html-decoded tag contents from Giphy', () => { - const giphy = ` - - - - - - - - - `; - - assert.strictEqual( - 'I Cant Hear You Kobe Bryant GIF - Find & Share on GIPHY', - getTitleMetaTag(giphy) - ); - assert.strictEqual( - 'https://media.giphy.com/media/3o7qE8mq5bT9FQj7j2/giphy.gif', - getImageMetaTag(giphy) - ); - }); - - it('returns html-decoded tag contents from Tenor', () => { - const tenor = ` - - - - - - - - - `; - - assert.strictEqual( - 'Hopping Jumping GIF - Hopping Jumping Bird - Discover & Share GIFs', - getTitleMetaTag(tenor) - ); - assert.strictEqual( - 'https://media1.tenor.com/images/3772949a5b042e626d259f313fd1e9b8/tenor.gif?itemid=14834517', - getImageMetaTag(tenor) - ); - }); - - it('returns only the first tag', () => { - const html = ` - - `; - - assert.strictEqual('First Second Third', getTitleMetaTag(html)); - }); - - it('handles a newline in attribute value', () => { - const html = ` - - `; - - assert.strictEqual( - 'First thing\r\nSecond thing\nThird thing', - getTitleMetaTag(html) - ); - }); - - it('converts image url protocol http to https', () => { - const html = ` - - `; - - assert.strictEqual( - 'https://giphygifs.s3.amazonaws.com/media/APcFiiTrG0x2/200.gif', - getImageMetaTag(html) - ); - }); - }); - - describe('#findLinks', () => { - it('returns all links if no caretLocation is provided', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - - const expected = [ - 'https://github.com/signalapp/Signal-Desktop', - 'https://github.com/signalapp/Signal-Android', - ]; - - const actual = findLinks(text); - assert.deepEqual(expected, actual); - }); - - it('includes all links if cursor is not in a link', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - const caretLocation = 10; - - const expected = [ - 'https://github.com/signalapp/Signal-Desktop', - 'https://github.com/signalapp/Signal-Android', - ]; - - const actual = findLinks(text, caretLocation); - assert.deepEqual(expected, actual); - }); - - it('excludes a link not at the end if the caret is inside of it', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - const caretLocation = 30; - - const expected = ['https://github.com/signalapp/Signal-Android']; - - const actual = findLinks(text, caretLocation); - assert.deepEqual(expected, actual); - }); - - it('excludes a link not at the end if the caret is at its end', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - const caretLocation = 64; - - const expected = ['https://github.com/signalapp/Signal-Android']; - - const actual = findLinks(text, caretLocation); - assert.deepEqual(expected, actual); - }); - - it('excludes a link at the end of the caret is inside of it', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - const caretLocation = 100; - - const expected = ['https://github.com/signalapp/Signal-Desktop']; - - const actual = findLinks(text, caretLocation); - assert.deepEqual(expected, actual); - }); - - it('includes link at the end if cursor is at its end', () => { - const text = - 'Check out this link: https://github.com/signalapp/Signal-Desktop\nAnd this one too: https://github.com/signalapp/Signal-Android'; - const caretLocation = text.length; - - const expected = [ - 'https://github.com/signalapp/Signal-Desktop', - 'https://github.com/signalapp/Signal-Android', - ]; - - const actual = findLinks(text, caretLocation); - assert.deepEqual(expected, actual); - }); - }); - - describe('#isLinkSneaky', () => { - it('returns false for all-latin domain', () => { - const link = 'https://www.amazon.com'; - const actual = isLinkSneaky(link); - assert.strictEqual(actual, false); - }); - - it('returns true for Latin + Cyrillic domain', () => { - const link = 'https://www.aмazon.com'; - const actual = isLinkSneaky(link); - assert.strictEqual(actual, true); - }); - - it('returns true for Latin + Greek domain', () => { - const link = 'https://www.αpple.com'; - const actual = isLinkSneaky(link); - assert.strictEqual(actual, true); - }); - - it('returns true for Latin + High Greek domain', () => { - const link = `https://www.apple${String.fromCodePoint(0x101a0)}.com`; - const actual = isLinkSneaky(link); - assert.strictEqual(actual, true); - }); - }); -}); diff --git a/ts/components/ContactListItem.tsx b/ts/components/ContactListItem.tsx index 0cf81a6f4..fa60ce4e0 100644 --- a/ts/components/ContactListItem.tsx +++ b/ts/components/ContactListItem.tsx @@ -50,7 +50,12 @@ export class ContactListItem extends React.Component { const profileElement = !isMe && profileName && !name ? ( - ~ + ~ + ) : null; diff --git a/ts/components/conversation/ConversationHeader.tsx b/ts/components/conversation/ConversationHeader.tsx index a26aaa31b..c2d943a20 100644 --- a/ts/components/conversation/ConversationHeader.tsx +++ b/ts/components/conversation/ConversationHeader.tsx @@ -4,7 +4,6 @@ import { Avatar } from '../Avatar'; import { LocalizerType } from '../../types/Util'; import { - SessionIcon, SessionIconButton, SessionIconSize, SessionIconType, @@ -89,7 +88,6 @@ interface Props { onAvatarClick?: (userPubKey: string) => void; onUpdateGroupName: () => void; - i18n: LocalizerType; memberAvatars?: Array; // this is added by usingClosedConversationDetails } @@ -121,7 +119,6 @@ class ConversationHeader extends React.Component { public renderTitle() { const { phoneNumber, - i18n, profileName, isGroup, isPublic, @@ -132,6 +129,7 @@ class ConversationHeader extends React.Component { isKickedFromGroup, name, } = this.props; + const { i18n } = window; if (isMe) { return ( @@ -232,12 +230,9 @@ class ConversationHeader extends React.Component { } public renderSelectionOverlay() { - const { - onDeleteSelectedMessages, - onCloseOverlay, - isPublic, - i18n, - } = this.props; + const { onDeleteSelectedMessages, onCloseOverlay, isPublic } = this.props; + const { i18n } = window; + const isServerDeletable = isPublic; const deleteMessageButtonText = i18n( isServerDeletable ? 'deleteForEveryone' : 'delete' @@ -266,7 +261,7 @@ class ConversationHeader extends React.Component { } public render() { - const { id, isKickedFromGroup, selectionMode } = this.props; + const { isKickedFromGroup, selectionMode } = this.props; const triggerId = 'conversation-header'; return ( diff --git a/ts/components/conversation/Emojify.tsx b/ts/components/conversation/Emojify.tsx index 1f0c0cee7..6e6f93000 100644 --- a/ts/components/conversation/Emojify.tsx +++ b/ts/components/conversation/Emojify.tsx @@ -86,9 +86,9 @@ export class Emojify extends React.Component { const emojiText = match[0] ?? match[1]; results.push( - + { if (type === SectionType.Profile) { const ourPrimary = window.storage.get('primaryDevicePubKey'); - const conversation = window.ConversationController.getOrThrow(ourPrimary); - const profile = conversation.getLokiProfile(); + const conversation = window.ConversationController.get(ourPrimary); + + const profile = conversation?.getLokiProfile(); const userName = (profile && profile.displayName) || ourPrimary; return ( {
diff --git a/ts/components/session/conversation/SessionConversation.tsx b/ts/components/session/conversation/SessionConversation.tsx index 6ad0a46f3..a8d0d5987 100644 --- a/ts/components/session/conversation/SessionConversation.tsx +++ b/ts/components/session/conversation/SessionConversation.tsx @@ -412,7 +412,6 @@ export class SessionConversation extends React.Component { const members = conversation.get('members') || []; const headerProps = { - i18n: window.i18n, id: conversation.id, name: conversation.getName(), phoneNumber: conversation.getNumber(), diff --git a/ts/window.d.ts b/ts/window.d.ts index 139336c86..1d95b0f8b 100644 --- a/ts/window.d.ts +++ b/ts/window.d.ts @@ -26,6 +26,7 @@ declare global { interface Window { CONSTANTS: any; ConversationController: ConversationControllerType; + SignalProtocolStore: any; Events: any; Lodash: any; LokiAppDotNetServerAPI: any;