diff --git a/js/models/conversations.js b/js/models/conversations.js index d3abe1952..6ffcc480b 100644 --- a/js/models/conversations.js +++ b/js/models/conversations.js @@ -565,6 +565,50 @@ }) ); }, + // This will add a message which will allow the user to reply to a friend request + // TODO: Maybe add callbacks for accept and decline? + async addFriendRequest() { + if (this.isMe()) { + window.log.info( + 'refusing to send friend request to ourselves' + ); + return; + } + + const lastMessage = this.get('timestamp') || Date.now(); + + window.log.info( + 'adding friend request for', + this.ourNumber, + this.idForLogging(), + lastMessage + ); + + const timestamp = Date.now(); + const message = { + conversationId: this.id, + type: 'friend-request', + sent_at: lastMessage, + received_at: timestamp, + unread: 1, + source: this.id, + target: this.ourNumber, + status: 'pending', + type: 'incoming', + }; + + const id = await window.Signal.Data.saveMessage(message, { + Message: Whisper.Message, + }); + + this.trigger( + 'newmessage', + new Whisper.Message({ + ...message, + id, + }) + ); + }, async addVerifiedChange(verifiedChangeId, verified, providedOptions) { const options = providedOptions || {}; _.defaults(options, { local: true }); diff --git a/js/models/messages.js b/js/models/messages.js index 2beca3b94..6cb97cedf 100644 --- a/js/models/messages.js +++ b/js/models/messages.js @@ -187,6 +187,9 @@ isKeyChange() { return this.get('type') === 'keychange'; }, + isFriendRequest() { + return this.get('type') === 'friend-request'; + }, getNotificationText() { const description = this.getDescription(); if (description) { @@ -292,6 +295,19 @@ // It doesn't need anything right now! return {}; }, + getPropsForFriendRequest() { + const source = this.get('source'); + const target = this.get('target'); + const status = this.get('status') || 'pending'; + const type = this.get('requestType') || 'incoming'; + + return { + source: this.findAndFormatContact(source), + target: this.findAndFormatContact(target), + status, + type, + } + }, findContact(phoneNumber) { return ConversationController.get(phoneNumber); }, diff --git a/js/modules/signal.js b/js/modules/signal.js index fcc4d022d..d176986b6 100644 --- a/js/modules/signal.js +++ b/js/modules/signal.js @@ -28,6 +28,9 @@ const { EmbeddedContact, } = require('../../ts/components/conversation/EmbeddedContact'); const { Emojify } = require('../../ts/components/conversation/Emojify'); +const { + FriendRequest, +} = require('../../ts/components/conversation/FriendRequest'); const { GroupNotification, } = require('../../ts/components/conversation/GroupNotification'); @@ -176,6 +179,7 @@ exports.setup = (options = {}) => { ConversationListItem, EmbeddedContact, Emojify, + FriendRequest, GroupNotification, Lightbox, LightboxGallery, diff --git a/js/views/message_view.js b/js/views/message_view.js index b260b9ba1..91b4b662b 100644 --- a/js/views/message_view.js +++ b/js/views/message_view.js @@ -69,6 +69,11 @@ Component: Components.GroupNotification, props: this.model.getPropsForGroupNotification(), }; + } else if (this.model.isFriendRequest()) { + return { + Component: Components.FriendRequest, + props: this.model.getPropsForFriendRequest(), + }; } return { diff --git a/ts/components/conversation/FriendRequest.tsx b/ts/components/conversation/FriendRequest.tsx new file mode 100644 index 000000000..03dd58504 --- /dev/null +++ b/ts/components/conversation/FriendRequest.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +// import classNames from 'classnames'; + +import { ContactName } from './ContactName'; +import { Intl } from '../Intl'; +import { Localizer } from '../../types/Util'; + +interface Contact { + phoneNumber: string; + profileName?: string; + name?: string; +} + +interface Props { + type: 'incoming' | 'outgoing'; + source: Contact; + target: Contact; + i18n: Localizer; + status: 'pending' | 'accepted' | 'declined'; +} + +export class FriendRequest extends React.Component { + public getStringId() { + const { status } = this.props; + + return 'youMarkedAsNotVerified'; + + switch (status) { + case 'pending': + return 'friendRequestPending'; + case 'accepted': + return 'friendRequestAccepted'; + case 'declined': + return 'friendRequestDeclined' + default: + // throw missingCaseError(status); + } + } + + public renderContents() { + const { source, i18n } = this.props; + const id = this.getStringId(); + + return ( + , + ]} + i18n={i18n} + /> + ); + } + + public render() { + const { type } = this.props; + + return ( +
+
+
+ {this.renderContents()} +
+
+
+ ); + } +}