remove plenty of the friend logic

This commit is contained in:
Audric Ackermann 2020-06-15 18:05:14 +10:00
parent bccdc3cf34
commit 66de8d9648
No known key found for this signature in database
GPG key ID: 999F434D76324AD4
36 changed files with 105 additions and 1285 deletions

View file

@ -102,7 +102,6 @@ module.exports = grunt => {
libloki: {
src: [
'libloki/api.js',
'libloki/friends.js',
'libloki/crypto.js',
'libloki/service_nodes.js',
'libloki/storage.js',

View file

@ -110,8 +110,6 @@ module.exports = {
updateConversation,
removeConversation,
getAllConversations,
getPubKeysWithFriendStatus,
getConversationsWithFriendStatus,
getAllRssFeedConversations,
getAllPublicConversations,
getPublicConversationsByServer,
@ -471,10 +469,10 @@ async function updateToSchemaVersion6(currentVersion, instance) {
console.log('updateToSchemaVersion6: starting...');
await instance.run('BEGIN TRANSACTION;');
await instance.run(
`ALTER TABLE conversations
ADD COLUMN friendRequestStatus INTEGER;`
);
// await instance.run(
// `ALTER TABLE conversations
// ADD COLUMN friendRequestStatus INTEGER;`
// );
await instance.run(
`CREATE TABLE lastHashes(
@ -837,7 +835,7 @@ async function updateToLokiSchemaVersion1(currentVersion, instance) {
const initConversation = async data => {
// eslint-disable-next-line camelcase
const { id, active_at, type, name, friendRequestStatus } = data;
const { id, active_at, type, name } = data;
await instance.run(
`INSERT INTO conversations (
id,
@ -845,8 +843,7 @@ async function updateToLokiSchemaVersion1(currentVersion, instance) {
active_at,
type,
members,
name,
friendRequestStatus
name
) values (
$id,
$json,
@ -854,7 +851,6 @@ async function updateToLokiSchemaVersion1(currentVersion, instance) {
$type,
$members,
$name,
$friendRequestStatus
);`,
{
$id: id,
@ -863,7 +859,6 @@ async function updateToLokiSchemaVersion1(currentVersion, instance) {
$type: type,
$members: null,
$name: name,
$friendRequestStatus: friendRequestStatus,
}
);
};
@ -877,7 +872,6 @@ async function updateToLokiSchemaVersion1(currentVersion, instance) {
const baseData = {
active_at: Date.now(),
friendRequestStatus: 4, // Friends
sealedSender: 0,
sessionResetStatus: 0,
swarmNodes: [],
@ -1860,7 +1854,6 @@ async function saveConversation(data) {
type,
members,
name,
friendRequestStatus,
profileName,
} = data;
@ -1873,7 +1866,6 @@ async function saveConversation(data) {
type,
members,
name,
friendRequestStatus,
profileName
) values (
$id,
@ -1883,7 +1875,6 @@ async function saveConversation(data) {
$type,
$members,
$name,
$friendRequestStatus,
$profileName
);`,
{
@ -1894,7 +1885,6 @@ async function saveConversation(data) {
$type: type,
$members: members ? members.join(' ') : null,
$name: name,
$friendRequestStatus: friendRequestStatus,
$profileName: profileName,
}
);
@ -1924,7 +1914,6 @@ async function updateConversation(data) {
type,
members,
name,
friendRequestStatus,
profileName,
} = data;
@ -1936,7 +1925,6 @@ async function updateConversation(data) {
type = $type,
members = $members,
name = $name,
friendRequestStatus = $friendRequestStatus,
profileName = $profileName
WHERE id = $id;`,
{
@ -1947,7 +1935,6 @@ async function updateConversation(data) {
$type: type,
$members: members ? members.join(' ') : null,
$name: name,
$friendRequestStatus: friendRequestStatus,
$profileName: profileName,
}
);
@ -2028,31 +2015,6 @@ async function getAllConversations() {
return map(rows, row => jsonToObject(row.json));
}
async function getPubKeysWithFriendStatus(status) {
const rows = await db.all(
`SELECT id FROM ${CONVERSATIONS_TABLE} WHERE
friendRequestStatus = $status
AND type = 'private'
ORDER BY id ASC;`,
{
$status: status,
}
);
return map(rows, row => row.id);
}
async function getConversationsWithFriendStatus(status) {
const rows = await db.all(
`SELECT * FROM ${CONVERSATIONS_TABLE} WHERE
friendRequestStatus = $status
AND type = 'private'
ORDER BY id ASC;`,
{
$status: status,
}
);
return map(rows, row => jsonToObject(row.json));
}
async function getAllConversationIds() {
const rows = await db.all(
@ -2521,7 +2483,7 @@ async function getMessageBySender({ source, sourceDevice, sent_at }) {
async function getAllUnsentMessages() {
const rows = await db.all(`
SELECT json FROM messages WHERE
type IN ('outgoing', 'friend-request') AND
type IN ('outgoing', 'session-request') AND
NOT sent
ORDER BY sent_at DESC;
`);

View file

@ -257,8 +257,6 @@
promises.concat([
conversation.updateProfileName(),
conversation.updateProfileAvatar(),
conversation.resetPendingSend(),
conversation.setFriendRequestExpiryTimeout(),
]);
});
await Promise.all(promises);

View file

@ -12,10 +12,8 @@ interface ConversationAttributes {
export interface ConversationModel
extends Backbone.Model<ConversationAttributes> {
setFriendRequestStatus: (status: any) => Promise<void>;
idForLogging: () => string;
saveChangesToDB: () => Promise<void>;
notifyFriendRequest: (source: string, type: string) => Promise<void>;
notify: (message: MessageModel) => void;
isSessionResetReceived: () => boolean;
updateExpirationTimer: (
@ -30,10 +28,6 @@ export interface ConversationModel
getRecipients: () => Array<string>;
onReadMessage: (message: MessageModel) => void;
updateTextInputState: () => void;
isFriend: () => boolean;
hasSentFriendRequest: () => boolean;
onFriendRequestAccepted: () => Promise<void>;
onFriendRequestReceived: () => Promise<void>;
lastMessage: string;
}

View file

@ -4,7 +4,7 @@
log,
i18n,
Backbone,
libloki,
libsession,
ConversationController,
MessageController,
storage,
@ -50,9 +50,6 @@
deleteAttachmentData,
} = window.Signal.Migrations;
// Possible conversation friend states
const FriendRequestStatusEnum = window.friends.friendRequestStatusEnum;
// Possible session reset states
const SessionResetEnum = Object.freeze({
// No ongoing reset
@ -83,8 +80,6 @@
return {
unreadCount: 0,
verified: textsecure.storage.protocol.VerifiedStatus.DEFAULT,
friendRequestStatus: FriendRequestStatusEnum.none,
unlockTimestamp: null, // Timestamp used for expiring friend requests.
sessionResetStatus: SessionResetEnum.none,
swarmNodes: [],
groupAdmins: [],
@ -181,10 +176,6 @@
this.typingRefreshTimer = null;
this.typingPauseTimer = null;
if (this.id === this.ourNumber) {
this.set({ friendRequestStatus: FriendRequestStatusEnum.friends });
}
this.messageSendQueue = new JobQueue();
this.selectedMessages = new Set();
@ -249,48 +240,6 @@
this.trigger('change');
this.messageCollection.forEach(m => m.trigger('change'));
},
async acceptFriendRequest() {
// Friend request message conmfirmations (Accept / Decline) are always
// sent to the primary device conversation
const messages = await window.Signal.Data.getMessagesByConversation(
this.getPrimaryDevicePubKey(),
{
limit: 5,
MessageCollection: Whisper.MessageCollection,
type: 'friend-request',
}
);
let lastMessage = null;
messages.forEach(m => {
m.acceptFriendRequest();
lastMessage = m;
});
if (lastMessage) {
await this.markRead();
window.Whisper.events.trigger(
'showConversation',
this.id,
lastMessage.id
);
}
},
async declineFriendRequest() {
const messages = await window.Signal.Data.getMessagesByConversation(
this.id,
{
limit: 1,
MessageCollection: Whisper.MessageCollection,
type: 'friend-request',
}
);
const lastMessageModel = messages.at(0);
if (lastMessageModel) {
lastMessageModel.declineFriendRequest();
}
},
setMessageSelectionBackdrop() {
const messageSelected = this.selectedMessages.size > 0;
@ -363,9 +312,12 @@
},
async bumpTyping() {
// We don't send typing messages if the setting is disabled or we aren't friends
const hasFriendDevice = await this.isFriendWithAnyDevice();
if (!storage.get('typing-indicators-setting') || !hasFriendDevice) {
// We don't send typing messages if the setting is disabled or we do not have a session
// or we blocked that user
const devicePubkey = new libsession.Types.PubKey(this.id);
const hasSession = await libsession.Protocols.SessionProtocol.hasSession(devicePubkey);
if (!storage.get('typing-indicators-setting') || !hasSession || this.isBlocked()) {
return;
}
@ -549,36 +501,6 @@
: `${message.source}.${message.sourceDevice}`;
this.clearContactTypingTimer(identifier);
},
// This goes through all our message history and finds a friend request
async getFriendRequests(direction = null, status = ['pending']) {
// Theoretically all our messages could be friend requests,
// thus we have to unfortunately go through each one :(
const messages = await window.Signal.Data.getMessagesByConversation(
this.id,
{
type: 'friend-request',
MessageCollection: Whisper.MessageCollection,
}
);
if (typeof status === 'string') {
// eslint-disable-next-line no-param-reassign
status = [status];
}
// Get the pending friend requests that match the direction
// If no direction is supplied then return all pending friend requests
return messages.models.filter(m => {
if (!status.includes(m.get('friendStatus'))) {
return false;
}
return direction === null || m.get('direction') === direction;
});
},
async getPendingFriendRequests(direction = null) {
return this.getFriendRequests(direction, ['pending']);
},
addSingleMessage(message, setToExpire = true) {
const model = this.messageCollection.add(message, { merge: true });
if (setToExpire) {
@ -614,9 +536,6 @@
title: this.getTitle(),
unreadCount: this.get('unreadCount') || 0,
mentionedUs: this.get('mentionedUs') || false,
isPendingFriendRequest: this.isPendingFriendRequest(),
hasReceivedFriendRequest: this.hasReceivedFriendRequest(),
hasSentFriendRequest: this.hasSentFriendRequest(),
isBlocked: this.isBlocked(),
isSecondary: !!this.get('secondaryStatus'),
primaryDevice: this.getPrimaryDevicePubKey(),
@ -630,7 +549,6 @@
},
isOnline: this.isOnline(),
hasNickname: !!this.getNickname(),
isFriend: !!this.isFriendWithAnyCache,
selectedMessages: this.selectedMessages,
@ -643,12 +561,8 @@
onDeleteContact: () => this.deleteContact(),
onDeleteMessages: () => this.deleteMessages(),
onCloseOverlay: () => this.resetMessageSelection(),
acceptFriendRequest: () => this.acceptFriendRequest(),
declineFriendRequest: () => this.declineFriendRequest(),
};
this.updateAsyncPropsCache();
return result;
},
@ -792,85 +706,6 @@
return contact.isVerified();
});
},
isFriendRequestStatusNone() {
return this.get('friendRequestStatus') === FriendRequestStatusEnum.none;
},
isFriendRequestStatusNoneOrExpired() {
const status = this.get('friendRequestStatus');
return (
status === FriendRequestStatusEnum.none ||
status === FriendRequestStatusEnum.requestExpired
);
},
isPendingFriendRequest() {
const status = this.get('friendRequestStatus');
return (
status === FriendRequestStatusEnum.requestSent ||
status === FriendRequestStatusEnum.requestReceived ||
status === FriendRequestStatusEnum.pendingSend
);
},
hasSentFriendRequest() {
const status = this.get('friendRequestStatus');
return (
status === FriendRequestStatusEnum.pendingSend ||
status === FriendRequestStatusEnum.requestSent
);
},
hasReceivedFriendRequest() {
return (
this.get('friendRequestStatus') ===
FriendRequestStatusEnum.requestReceived
);
},
isFriend() {
return (
this.get('friendRequestStatus') === FriendRequestStatusEnum.friends
);
},
async getAnyDeviceFriendRequestStatus() {
const secondaryDevices = await window.libloki.storage.getSecondaryDevicesFor(
this.id
);
const allDeviceStatus = secondaryDevices
// Get all the secondary device friend status'
.map(pubKey => {
const conversation = ConversationController.get(pubKey);
if (!conversation) {
return FriendRequestStatusEnum.none;
}
return conversation.getFriendRequestStatus();
})
// Also include this conversation's friend status
.concat(this.get('friendRequestStatus'))
.reduce((acc, cur) => {
if (
acc === FriendRequestStatusEnum.friends ||
cur === FriendRequestStatusEnum.friends
) {
return FriendRequestStatusEnum.friends;
}
if (acc !== FriendRequestStatusEnum.none) {
return acc;
}
return cur;
}, FriendRequestStatusEnum.none);
return allDeviceStatus;
},
async updateAsyncPropsCache() {
const isFriendWithAnyDevice = await this.isFriendWithAnyDevice();
if (this.isFriendWithAnyCache !== isFriendWithAnyDevice) {
this.isFriendWithAnyCache = isFriendWithAnyDevice;
this.trigger('change');
}
},
async isFriendWithAnyDevice() {
const allDeviceStatus = await this.getAnyDeviceFriendRequestStatus();
return allDeviceStatus === FriendRequestStatusEnum.friends;
},
getFriendRequestStatus() {
return this.get('friendRequestStatus');
},
async getPrimaryConversation() {
if (!this.isSecondaryDevice()) {
// This is already the primary conversation
@ -900,8 +735,6 @@
primaryConversation.updateTextInputState();
return;
}
const allDeviceStatus = await this.getAnyDeviceFriendRequestStatus();
if (this.get('isKickedFromGroup')) {
this.trigger('disable:input', true);
return;
@ -911,25 +744,10 @@
this.trigger('change:placeholder', 'left-group');
return;
}
switch (allDeviceStatus) {
case FriendRequestStatusEnum.none:
case FriendRequestStatusEnum.requestExpired:
this.trigger('disable:input', false);
this.trigger('change:placeholder', 'friend-request');
return;
case FriendRequestStatusEnum.pendingSend:
case FriendRequestStatusEnum.requestReceived:
case FriendRequestStatusEnum.requestSent:
this.trigger('disable:input', true);
this.trigger('change:placeholder', 'disabled');
return;
case FriendRequestStatusEnum.friends:
this.trigger('disable:input', false);
this.trigger('change:placeholder', 'chat');
return;
default:
throw new Error('Invalid friend request state');
}
// otherwise, enable the input and set default placeholder
this.trigger('disable:input', false);
this.trigger('change:placeholder', 'chat');
},
isSecondaryDevice() {
return !!this.get('secondaryStatus');
@ -948,253 +766,12 @@
});
}
},
async setFriendRequestStatus(newStatus, options = {}) {
const { blockSync } = options;
// Ensure that the new status is a valid FriendStatusEnum value
if (!(newStatus in Object.values(FriendRequestStatusEnum))) {
return;
}
if (
this.ourNumber === this.id &&
newStatus !== FriendRequestStatusEnum.friends
) {
return;
}
if (this.get('friendRequestStatus') !== newStatus) {
this.set({ friendRequestStatus: newStatus });
if (newStatus === FriendRequestStatusEnum.friends) {
if (!blockSync) {
// Sync contact
this.wrapSend(textsecure.messaging.sendContactSyncMessage([this]));
}
// Only enable sending profileKey after becoming friends
this.set({ profileSharing: true });
}
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
await this.updateTextInputState();
}
},
async updateGroupAdmins(groupAdmins) {
this.set({ groupAdmins });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
},
async updateAllFriendRequestsMessages(options) {
const { response, status, direction = null } = options;
// Ignore if no response supplied
if (!response) {
return;
}
// Accept FRs from all the user's devices
const allDevices = await libloki.storage.getAllDevicePubKeysForPrimaryPubKey(
this.getPrimaryDevicePubKey()
);
if (!allDevices.length) {
return;
}
const allConversationsWithUser = allDevices.map(d =>
ConversationController.get(d)
);
// Search through each conversation (device) for friend request messages
const pendingRequestPromises = allConversationsWithUser.map(
async conversation => {
const request = (
await conversation.getFriendRequests(direction, status)
)[0];
return { conversation, request };
}
);
let pendingRequests = await Promise.all(pendingRequestPromises);
// Filter out all undefined requests
pendingRequests = pendingRequests.filter(p => Boolean(p.request));
// We set all friend request messages from all devices
// from a user here to accepted where possible
await Promise.all(
pendingRequests.map(async friendRequest => {
const { conversation, request } = friendRequest;
if (request.hasErrors()) {
return;
}
request.set({ friendStatus: response });
await window.Signal.Data.saveMessage(request.attributes, {
Message: Whisper.Message,
});
conversation.trigger('updateMessage', request);
})
);
},
async updateAllPendingFriendRequestsMessages(options) {
return this.updateAllFriendRequestsMessages({
...options,
status: 'pending',
});
},
async resetPendingSend() {
if (
this.get('friendRequestStatus') === FriendRequestStatusEnum.pendingSend
) {
await this.setFriendRequestStatus(FriendRequestStatusEnum.none);
}
},
// We have declined an incoming friend request
async onDeclineFriendRequest() {
this.setFriendRequestStatus(FriendRequestStatusEnum.none);
await this.updateAllPendingFriendRequestsMessages({
response: 'declined',
direction: 'incoming',
});
await window.libloki.storage.removeContactPreKeyBundle(this.id);
await this.destroyMessages();
window.pushToast({
title: i18n('friendRequestDeclined'),
type: 'success',
id: 'declineFriendRequest',
});
},
// We have accepted an incoming friend request
async onAcceptFriendRequest(options = {}) {
if (this.get('type') !== Message.PRIVATE) {
return;
}
if (this.unlockTimer) {
clearTimeout(this.unlockTimer);
}
if (this.hasReceivedFriendRequest()) {
this.setFriendRequestStatus(FriendRequestStatusEnum.friends, options);
await this.updateAllFriendRequestsMessages({
response: 'accepted',
direction: 'incoming',
status: ['pending', 'expired'],
});
window.libloki.api.sendBackgroundMessage(
this.id,
window.textsecure.OutgoingMessage.DebugMessageType
.INCOMING_FR_ACCEPTED
);
} else if (this.isFriendRequestStatusNoneOrExpired()) {
// send AFR if we haven't sent a message before
const autoFrMessage = textsecure.OutgoingMessage.buildAutoFriendRequestMessage(
this.id
);
await autoFrMessage.sendToNumber(this.id, false);
}
},
// Our outgoing friend request has been accepted
async onFriendRequestAccepted() {
if (this.isFriend()) {
return false;
}
if (this.unlockTimer) {
clearTimeout(this.unlockTimer);
}
if (this.hasSentFriendRequest()) {
this.setFriendRequestStatus(FriendRequestStatusEnum.friends);
await this.updateAllFriendRequestsMessages({
response: 'accepted',
status: ['pending', 'expired'],
});
window.libloki.api.sendBackgroundMessage(
this.id,
window.textsecure.OutgoingMessage.DebugMessageType
.OUTGOING_FR_ACCEPTED
);
return true;
}
return false;
},
async onFriendRequestTimeout() {
// Unset the timer
if (this.unlockTimer) {
clearTimeout(this.unlockTimer);
}
this.unlockTimer = null;
if (this.isFriend()) {
return;
}
// Set the unlock timestamp to null
if (this.get('unlockTimestamp')) {
this.set({ unlockTimestamp: null });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
}
// Change any pending outgoing friend requests to expired
await this.updateAllPendingFriendRequestsMessages({
response: 'expired',
direction: 'outgoing',
});
await this.setFriendRequestStatus(FriendRequestStatusEnum.requestExpired);
},
async onFriendRequestReceived() {
if (this.isFriendRequestStatusNone()) {
this.setFriendRequestStatus(FriendRequestStatusEnum.requestReceived);
} else if (this.hasSentFriendRequest()) {
await Promise.all([
this.setFriendRequestStatus(FriendRequestStatusEnum.friends),
// Accept all outgoing FR
this.updateAllPendingFriendRequestsMessages({
direction: 'outgoing',
response: 'accepted',
}),
]);
}
// Delete stale incoming friend requests
const incoming = await this.getPendingFriendRequests('incoming');
await Promise.all(
incoming.map(request => this._removeMessage(request.id))
);
this.trigger('change');
},
async onFriendRequestSent() {
// Check if we need to set the friend request expiry
const unlockTimestamp = this.get('unlockTimestamp');
if (!this.isFriend() && !unlockTimestamp) {
// Expire the messages after 72 hours
const hourLockDuration = 72;
const ms = 60 * 60 * 1000 * hourLockDuration;
this.set({ unlockTimestamp: Date.now() + ms });
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
this.setFriendRequestExpiryTimeout();
}
await this.setFriendRequestStatus(FriendRequestStatusEnum.requestSent);
},
friendRequestTimerIsExpired() {
const unlockTimestamp = this.get('unlockTimestamp');
if (unlockTimestamp && unlockTimestamp > Date.now()) {
return false;
}
return true;
},
setFriendRequestExpiryTimeout() {
if (this.isFriend()) {
return;
}
const unlockTimestamp = this.get('unlockTimestamp');
if (unlockTimestamp && !this.unlockTimer) {
const delta = Math.max(unlockTimestamp - Date.now(), 0);
this.unlockTimer = setTimeout(() => {
this.onFriendRequestTimeout();
}, delta);
}
},
isUnverified() {
if (this.isPrivate()) {
const verified = this.get('verified');
@ -1614,73 +1191,18 @@
);
const conversationType = this.get('type');
let messageWithSchema = null;
// If we are a friend with any of the devices, send the message normally
const canSendNormalMessage = await this.isFriendWithAnyDevice();
const isGroup = conversationType === Message.GROUP;
if (canSendNormalMessage || isGroup) {
messageWithSchema = await upgradeMessageSchema({
type: 'outgoing',
body,
conversationId: destination,
quote,
preview,
attachments,
sent_at: now,
received_at: now,
expireTimer,
recipients,
});
} else {
// Check if we have sent a friend request
const outgoingRequests = await this.getPendingFriendRequests(
'outgoing'
);
if (outgoingRequests.length > 0) {
// Check if the requests have errored, if so then remove them
// and send the new request if possible
let friendRequestSent = false;
const promises = [];
outgoingRequests.forEach(outgoing => {
if (outgoing.hasErrors()) {
promises.push(this._removeMessage(outgoing.id));
} else {
// No errors = we have sent over the friend request
friendRequestSent = true;
}
});
await Promise.all(promises);
// If the requests didn't error then don't add a new friend request
// because one of them was sent successfully
if (friendRequestSent) {
return null;
}
}
await this.setFriendRequestStatus(
FriendRequestStatusEnum.pendingSend
);
// Always share our profileKey in the friend request
// This will get added automatically after the FR
// is accepted, via the profileSharing flag
profileKey = storage.get('profileKey');
// Send the friend request!
messageWithSchema = await upgradeMessageSchema({
type: 'friend-request',
body,
conversationId: destination,
sent_at: now,
received_at: now,
expireTimer,
recipients,
direction: 'outgoing',
friendStatus: 'pending',
});
}
const messageWithSchema = await upgradeMessageSchema({
type: 'outgoing',
body,
conversationId: destination,
quote,
preview,
attachments,
sent_at: now,
received_at: now,
expireTimer,
recipients,
});
if (this.isPrivate()) {
messageWithSchema.destination = destination;
@ -1880,12 +1402,7 @@
async handleMessageSendResult({
failoverNumbers,
unidentifiedDeliveries,
messageType,
success,
}) {
if (success && messageType === 'friend-request') {
await this.onFriendRequestSent();
}
await Promise.all(
(failoverNumbers || []).map(async number => {
const conversation = ConversationController.get(number);
@ -2491,8 +2008,9 @@
// conversation is viewed, another error message shows up for the contact
read = read.filter(item => !item.hasErrors);
// Do not send read receipt if not friends yet
if (!this.isFriendWithAnyDevice()) {
const devicePubkey = new libsession.Types.PubKey(this.id);
const hasSession = await libsession.Protocols.SessionProtocol.hasSession(devicePubkey);
if (hasSession) {
return;
}
@ -2592,7 +2110,7 @@
this.get('server') !== newServer ||
this.get('channelId') !== newChannelId
) {
// mark active so it's not in the friends list but the conversation list
// mark active so it's not in the contacts list but in the conversation list
this.set({
server: newServer,
channelId: newChannelId,
@ -2985,11 +2503,6 @@
});
}
// Reset our friend status if we're not friends
if (!this.isFriend()) {
this.set({ friendRequestStatus: FriendRequestStatusEnum.none });
}
await window.Signal.Data.updateConversation(this.id, this.attributes, {
Conversation: Whisper.Conversation,
});
@ -3113,12 +2626,6 @@
},
notify(message) {
if (message.isFriendRequest()) {
if (this.hasSentFriendRequest()) {
return this.notifyFriendRequest(message.get('source'), 'accepted');
}
return this.notifyFriendRequest(message.get('source'), 'requested');
}
if (!message.isIncoming()) {
return Promise.resolve();
}
@ -3151,55 +2658,6 @@
})
);
},
// Notification for friend request received
async notifyFriendRequest(source, type) {
// Data validation
if (!source) {
throw new Error('Invalid source');
}
if (!['accepted', 'requested'].includes(type)) {
throw new Error('Type must be accepted or requested.');
}
// Call the notification on the right conversation
let conversation = this;
if (conversation.id !== source) {
try {
conversation = await ConversationController.getOrCreateAndWait(
source,
'private'
);
window.log.info(`Notify called on a different conversation.
Expected: ${this.id}. Actual: ${conversation.id}`);
} catch (e) {
throw new Error('Failed to fetch conversation.');
}
}
const isTypeAccepted = type === 'accepted';
const title = isTypeAccepted
? 'friendRequestAcceptedNotificationTitle'
: 'friendRequestNotificationTitle';
const message = isTypeAccepted
? 'friendRequestAcceptedNotificationMessage'
: 'friendRequestNotificationMessage';
const iconUrl = await conversation.getNotificationIcon();
// window.log.info('Add notification for friend request updated', {
// conversationId: conversation.idForLogging(),
// });
Whisper.Notifications.add({
conversationId: conversation.id,
iconUrl,
isExpiringMessage: false,
message: i18n(message, conversation.getTitle()),
messageSentAt: Date.now(),
title: i18n(title),
isFriendRequest: true,
friendRequestType: type,
});
},
notifyTyping(options = {}) {
const { isTyping, sender, senderDevice } = options;

View file

@ -277,8 +277,8 @@
isKeyChange() {
return this.get('type') === 'keychange';
},
isFriendRequest() {
return this.get('type') === 'friend-request';
isSessionRequest() {
return this.get('type') === 'session-request';
},
isGroupInvitation() {
return !!this.get('groupInvitation');

View file

@ -128,8 +128,6 @@ module.exports = {
_removeConversations,
getAllConversations,
getPubKeysWithFriendStatus,
getConversationsWithFriendStatus,
getAllConversationIds,
getAllPrivateConversations,
getAllRssFeedConversations,
@ -826,22 +824,6 @@ async function _removeConversations(ids) {
await channels.removeConversation(ids);
}
async function getConversationsWithFriendStatus(
status,
{ ConversationCollection }
) {
const conversations = await channels.getConversationsWithFriendStatus(status);
const collection = new ConversationCollection();
collection.add(conversations);
return collection;
}
async function getPubKeysWithFriendStatus(status) {
const conversations = await getConversationsWithFriendStatus(status);
return conversations.map(row => row.id);
}
async function getAllConversations({ ConversationCollection }) {
const conversations = await channels.getAllConversations();

View file

@ -92,9 +92,9 @@
let message;
let iconUrl;
// The number of notifications excluding friend request
// The number of notifications excluding session request
const messagesNotificationCount = this.models.filter(
n => !n.get('isFriendRequest')
n => !n.get('isSessionRequest')
).length;
// NOTE: i18n has more complex rules for pluralization than just
@ -112,9 +112,7 @@
case SettingNames.COUNT:
title = 'Session';
if (last.isFriendRequest) {
message = `Friend request ${last.friendRequestType}`;
} else if (messagesNotificationCount > 0) {
if (messagesNotificationCount > 0) {
message = newMessageCountLabel;
} else {
return;
@ -125,7 +123,7 @@
title = newMessageCountLabel;
// eslint-disable-next-line prefer-destructuring
iconUrl = last.iconUrl;
if (last.isFriendRequest || messagesNotificationCount === 1) {
if (messagesNotificationCount === 1) {
message = `${i18n('notificationFrom')} ${lastMessageTitle}`;
} else {
message = `${i18n(
@ -135,7 +133,7 @@
break;
}
case SettingNames.MESSAGE:
if (last.isFriendRequest || messagesNotificationCount === 1) {
if (messagesNotificationCount === 1) {
// eslint-disable-next-line prefer-destructuring
title = last.title;
// eslint-disable-next-line prefer-destructuring

View file

@ -57,11 +57,6 @@
initialize(options) {
this.listenTo(this.model, 'destroy', this.stopListening);
this.listenTo(this.model, 'change:verified', this.onVerifiedChange);
this.listenTo(
this.model,
'change:friendRequestStatus',
this.onFriendStatusChange
);
this.listenTo(this.model, 'newmessage', this.addMessage);
this.listenTo(this.model, 'opened', this.onOpened);
this.listenTo(this.model, 'prune', this.onPrune);
@ -129,7 +124,6 @@
);
this.render();
this.onFriendStatusChange();
this.model.updateTextInputState();
@ -532,9 +526,6 @@
}
let placeholder;
switch (type) {
case 'friend-request':
placeholder = i18n('sendMessageFriendRequest');
break;
case 'disabled':
placeholder = i18n('sendMessageDisabled');
break;
@ -735,14 +726,6 @@
}
},
onFriendStatusChange() {
if (this.model.isPrivate() && !this.model.isFriend()) {
this.$('#choose-file').hide();
} else {
this.$('#choose-file').show();
}
},
async toggleMicrophone() {
const allowMicrophone = await window.getMediaPermissions();
if (

View file

@ -17,7 +17,7 @@
const convos = window.getConversations().models;
let allMembers = convos.filter(
d => !!d && d.isFriend() && d.isPrivate() && !d.isMe()
d => !!d && d.isPrivate() && !d.isMe()
);
allMembers = _.uniq(allMembers, true, d => d.id);

View file

@ -14,12 +14,12 @@
const convos = window.getConversations().models;
this.friends = convos.filter(
d => !!d && d.isFriend() && d.isPrivate() && !d.isMe()
this.contacts = convos.filter(
d => !!d && d.isPrivate() && !d.isMe()
);
if (!convo.isPublic()) {
const members = convo.get('members') || [];
this.friends = this.friends.filter(d => !members.includes(d.id));
this.contacts = this.contacts.filter(d => !members.includes(d.id));
}
this.chatName = convo.get('name');
@ -36,7 +36,7 @@
className: 'invite-friends-dialog',
Component: window.Signal.Components.InviteFriendsDialog,
props: {
friendList: this.friends,
friendList: this.contacts,
onSubmit: this.submit,
onClose: this.close,
chatName: this.chatName,

View file

@ -22,16 +22,15 @@
const convos = window.getConversations().models;
// private friends (not you) that aren't already moderators
const friends = convos.filter(
const contacts = convos.filter(
d =>
!!d &&
d.isFriend() &&
d.isPrivate() &&
!d.isMe() &&
!modPubKeys.includes(d.id)
);
this.friends = friends;
this.contacts = contacts;
this.$el.focus();
this.render();
@ -41,7 +40,7 @@
className: 'add-moderators-dialog',
Component: window.Signal.Components.AddModeratorsDialog,
props: {
friendList: this.friends,
friendList: this.contacts,
chatName: this.chatName,
onSubmit: this.onSubmit,
onClose: this.close,

View file

@ -100,13 +100,6 @@
await backgroundMessage.sendToNumber(pubKey, false);
}
async function sendAutoFriendRequestMessage(pubKey) {
const autoFrMessage = textsecure.OutgoingMessage.buildAutoFriendRequestMessage(
pubKey
);
await autoFrMessage.sendToNumber(pubKey, false);
}
function createPairingAuthorisationProtoMessage({
primaryDevicePubKey,
secondaryDevicePubKey,
@ -322,7 +315,6 @@
window.libloki.api = {
sendSessionEstablishedMessage,
sendBackgroundMessage,
sendAutoFriendRequestMessage,
sendSessionRequestsToMembers,
sendPairingAuthorisation,
createPairingAuthorisationProtoMessage,

View file

@ -1,24 +0,0 @@
/* global window */
// eslint-disable-next-line func-names
(function() {
// Possible conversation friend states
const friendRequestStatusEnum = Object.freeze({
// New conversation, no messages sent or received
none: 0,
// This state is used to lock the input early while sending
pendingSend: 1,
// Friend request sent, awaiting response
requestSent: 2,
// Friend request received, awaiting user input
requestReceived: 3,
// We did it!
friends: 4,
// Friend Request sent but timed out
requestExpired: 5,
});
window.friends = {
friendRequestStatusEnum,
};
})();

View file

@ -43,36 +43,4 @@ describe('Loki Messages', () => {
);
});
});
describe('#autoFriendRequestMessage', () => {
it('structure is valid', () => {
const pubkey =
'05050505050505050505050505050505050505050505050505050505050505050';
const autoFrMessage = window.textsecure.OutgoingMessage.buildAutoFriendRequestMessage(
pubkey
);
const validAutoFrObject = {
server: null,
numbers: [pubkey],
};
const validAutoFrMessage = {
syncMessage: null,
callMessage: null,
nullMessage: null,
receiptMessage: null,
typingMessage: null,
preKeyBundleMessage: null,
lokiAddressMessage: null,
pairingAuthorisation: null,
};
assert.isNumber(autoFrMessage.timestamp);
assert.isFunction(autoFrMessage.callback);
assert.deepInclude(autoFrMessage.message, validAutoFrMessage);
assert.isObject(autoFrMessage.message.dataMessage);
assert.deepInclude(autoFrMessage, validAutoFrObject);
});
});
});

View file

@ -611,13 +611,6 @@
throw e;
}
// Always be friends with secondary devices
await secondaryConversation.setFriendRequestStatus(
window.friends.friendRequestStatusEnum.friends,
{
blockSync: true,
}
);
// Send sync messages
// bad hack to send sync messages when secondary device is ready to process them
setTimeout(async () => {

View file

@ -2,7 +2,6 @@
textsecure,
libsignal,
window,
ConversationController,
libloki,
StringView,
lokiMessageAPI,
@ -15,8 +14,8 @@ const NUM_SEND_CONNECTIONS = 3;
const getTTLForType = type => {
switch (type) {
case 'friend-request':
return 4 * 24 * 60 * 60 * 1000; // 4 days for friend request message
case 'session-request':
return 4 * 24 * 60 * 60 * 1000; // 4 days for session request message
case 'device-unpairing':
return 4 * 24 * 60 * 60 * 1000; // 4 days for device unpairing
case 'onlineBroadcast':
@ -358,51 +357,11 @@ OutgoingMessage.prototype = {
const updatedDevices = await getStaleDeviceIdsForNumber(devicePubKey);
const keysFound = await this.getKeysForNumber(devicePubKey, updatedDevices);
// let isMultiDeviceRequest = false;
let thisDeviceMessageType = this.messageType;
if (
thisDeviceMessageType !== 'pairing-request' &&
thisDeviceMessageType !== 'friend-request' &&
thisDeviceMessageType !== 'onlineBroadcast'
) {
try {
const conversation = ConversationController.get(devicePubKey);
if (conversation && !this.isGroup) {
const isOurDevice = await conversation.isOurDevice();
const isFriends =
conversation.isFriend() || conversation.hasReceivedFriendRequest();
// We should only send a friend request to our device if we don't have keys
const shouldSendAutomatedFR = isOurDevice ? !keysFound : !isFriends;
if (shouldSendAutomatedFR) {
// We want to send an automated friend request if:
// - We aren't already friends
// - We haven't received a friend request from this device
// - We haven't sent a friend request recently
if (conversation.friendRequestTimerIsExpired()) {
// isMultiDeviceRequest = true;
thisDeviceMessageType = 'friend-request';
} else {
// Throttle automated friend requests
this.successfulNumbers.push(devicePubKey);
return null;
}
}
// If we're not friends with our own device then we should become friends
if (isOurDevice && keysFound && !isFriends) {
conversation.setFriendRequestStatus(
window.friends.friendRequestStatusEnum.friends
);
}
}
} catch (e) {
// do nothing
}
}
// Check if we need to attach the preKeys
const enableFallBackEncryption =
!keysFound || thisDeviceMessageType === 'friend-request';
!keysFound || this.messageType === 'session-request';
const flags = this.message.dataMessage
? this.message.dataMessage.get_flags()
: null;
@ -451,7 +410,7 @@ OutgoingMessage.prototype = {
// FIXME options not used at all; if (ourPubkey === number) {
// options.messageKeysLimit = false;
// }
const ttl = getTTLForType(thisDeviceMessageType);
const ttl = getTTLForType(this.messageType);
const ourKey = textsecure.storage.user.getNumber();
return {
@ -634,14 +593,6 @@ OutgoingMessage.prototype = {
this.timestamp,
ttl
);
if (!this.isGroup && !isSessionRequest) {
const conversation = ConversationController.get(destination);
if (conversation) {
// Redundant for primary device but marks secondary devices as pending
await conversation.onFriendRequestSent();
}
}
this.successfulNumbers.push(destination);
} catch (e) {
e.number = destination;
@ -691,14 +642,7 @@ OutgoingMessage.prototype = {
},
sendToNumber(number, multiDevice = true) {
let conversation;
try {
conversation = ConversationController.get(number);
} catch (e) {
// do nothing
}
return this.reloadDevicesAndSend(number, multiDevice).catch(error => {
conversation.resetPendingSend();
if (error.message === 'Identity key changed') {
// eslint-disable-next-line no-param-reassign
error = new textsecure.OutgoingIdentityKeyError(
@ -719,32 +663,6 @@ OutgoingMessage.prototype = {
},
};
OutgoingMessage.buildAutoFriendRequestMessage = function buildAutoFriendRequestMessage(
pubKey
) {
const body = 'Please accept to enable messages to be synced across devices';
const dataMessage = new textsecure.protobuf.DataMessage({ body });
const content = new textsecure.protobuf.Content({
dataMessage,
});
const options = {
messageType: 'friend-request',
debugMessageType: DebugMessageType.AUTO_FR_REQUEST,
};
// Send a empty message with information about how to contact us directly
return new OutgoingMessage(
null, // server
Date.now(), // timestamp,
[pubKey], // numbers
content, // message
true, // silent
() => null, // callback
options
);
};
OutgoingMessage.buildSessionRequestMessage = function buildSessionRequestMessage(
pubKey
) {
@ -760,7 +678,7 @@ OutgoingMessage.buildSessionRequestMessage = function buildSessionRequestMessage
});
const options = {
messageType: 'friend-request',
messageType: 'session-request',
debugMessageType: DebugMessageType.SESSION_REQUEST,
};
// Send a empty message with information about how to contact us directly

View file

@ -440,7 +440,7 @@ MessageSender.prototype = {
keysFound ||
options.isPublic ||
options.isMediumGroup ||
options.messageType === 'friend-request'
options.messageType === 'session-request'
) {
const outgoing = new OutgoingMessage(
this.server,
@ -672,7 +672,7 @@ MessageSender.prototype = {
c =>
c.isPrivate() &&
!c.isOurLocalDevice() &&
c.isFriend() &&
!c.isBlocked() &&
!c.get('secondaryStatus')
) || [];
@ -681,7 +681,7 @@ MessageSender.prototype = {
c =>
c.isPrivate() &&
!c.isOurLocalDevice() &&
c.isFriend() &&
!c.isBlocked() &&
c.get('secondaryStatus')
);
@ -757,7 +757,7 @@ MessageSender.prototype = {
c =>
c.isClosedGroup() &&
!c.get('left') &&
c.isFriend() &&
!c.isBlocked() &&
!c.isMediumGroup()
);
if (sessionGroups.length === 0) {
@ -1177,12 +1177,6 @@ MessageSender.prototype = {
throw error;
};
// Loki - Temp hack for new protocol
// A session reset should be a `SESSION_REQUEST`
const msgOptions = {
messageType: 'friend-request',
...options,
};
// The actual deletion of the session now happens later
// as we need to ensure the other contact has successfully
@ -1192,7 +1186,7 @@ MessageSender.prototype = {
proto,
timestamp,
silent,
msgOptions
options
).catch(logError('resetSession/sendToContact error:'));
},

View file

@ -565,7 +565,6 @@ describe('Backup', () => {
const ommited = [
'profileAvatar',
'swarmNodes',
'friendRequestStatus',
'groupAdmins',
'isKickedFromGroup',
'unlockTimestamp',

View file

@ -39,13 +39,9 @@ export type PropsData = {
isRss: boolean;
};
isPendingFriendRequest?: boolean;
hasReceivedFriendRequest?: boolean;
hasSentFriendRequest?: boolean;
isBlocked?: boolean;
isOnline?: boolean;
hasNickname?: boolean;
isFriend?: boolean;
isSecondary?: boolean;
isGroupInvitation?: boolean;
};
@ -61,8 +57,6 @@ type PropsHousekeeping = {
onClearNickname?: () => void;
onCopyPublicKey?: () => void;
onUnblockContact?: () => void;
acceptFriendRequest?: () => void;
declineFriendRequest?: () => void;
};
type Props = PropsData & PropsHousekeeping;
@ -79,15 +73,11 @@ export class ConversationListItem extends React.PureComponent<Props> {
phoneNumber,
profileName,
isOnline,
isPendingFriendRequest,
hasSentFriendRequest,
} = this.props;
let borderColor;
if (!(isPendingFriendRequest && !hasSentFriendRequest)) {
borderColor = isOnline ? Colors.ONLINE : Colors.OFFLINE;
}
const iconSize = isPendingFriendRequest && !hasSentFriendRequest ? 28 : 36;
const borderColor = isOnline ? Colors.ONLINE : Colors.OFFLINE;
const iconSize = 36;
return (
<div className="module-conversation-list-item__avatar-container">
@ -132,8 +122,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
i18n,
isMe,
lastUpdated,
isFriend,
hasReceivedFriendRequest,
} = this.props;
return (
@ -148,8 +136,8 @@ export class ConversationListItem extends React.PureComponent<Props> {
>
{isMe ? i18n('noteToSelf') : this.renderUser()}
</div>
{hasReceivedFriendRequest || this.renderUnread()}
{isFriend && (
{this.renderUnread()}
{(
<div
className={classNames(
'module-conversation-list-item__header__date',
@ -158,7 +146,7 @@ export class ConversationListItem extends React.PureComponent<Props> {
: null
)}
>
{!hasReceivedFriendRequest && (
{(
<Timestamp
timestamp={lastUpdated}
extended={false}
@ -231,7 +219,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
isTyping,
unreadCount,
i18n,
isPendingFriendRequest,
} = this.props;
if (!lastMessage && !isTyping) {
@ -245,10 +232,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
text = text.replace(/<[^>]*>?/gm, '');
}
if (isPendingFriendRequest) {
text = text.replace('Friend Request: ', '');
}
if (isEmpty(text)) {
return null;
}
@ -287,24 +270,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
);
}
public renderFriendRequestButtons() {
const { acceptFriendRequest, declineFriendRequest } = this.props;
return (
<div className="module-conversation-list-item__buttons">
<SessionButton
text={window.i18n('decline')}
buttonColor={SessionButtonColor.None}
onClick={declineFriendRequest}
/>
<SessionButton
text={window.i18n('accept')}
onClick={acceptFriendRequest}
/>
</div>
);
}
public render() {
const {
phoneNumber,
@ -312,7 +277,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
onClick,
id,
isSelected,
hasReceivedFriendRequest,
isBlocked,
style,
mentionedUs,
@ -340,9 +304,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
? 'module-conversation-list-item--mentioned-us'
: null,
isSelected ? 'module-conversation-list-item--is-selected' : null,
hasReceivedFriendRequest
? 'module-conversation-list-item--has-friend-request'
: null,
isBlocked ? 'module-conversation-list-item--is-blocked' : null
)}
>
@ -351,7 +312,6 @@ export class ConversationListItem extends React.PureComponent<Props> {
{this.renderHeader()}
{this.renderMessage()}
</div>
{hasReceivedFriendRequest && this.renderFriendRequestButtons()}
</div>
</ContextMenuTrigger>
<Portal>{this.renderContextMenu(triggerId)}</Portal>

View file

@ -29,11 +29,9 @@ interface State {
interface Props {
conversations: Array<ConversationListItemPropsType>;
friends: Array<ConversationType>;
sentFriendsRequest: Array<ConversationListItemPropsType>;
receivedFriendsRequest: Array<ConversationListItemPropsType>;
contacts: Array<ConversationType>;
unreadMessageCount: number;
receivedFriendRequestCount: number;
searchResults?: SearchResultsProps;
searchTerm: string;
isSecondaryDevice: boolean;
@ -88,7 +86,6 @@ export class LeftPane extends React.Component<Props, State> {
selectedSection={this.state.selectedSection}
onSectionSelected={this.handleSectionSelected}
conversations={this.props.conversations}
receivedFriendRequestCount={this.props.receivedFriendRequestCount}
unreadMessageCount={this.props.unreadMessageCount}
/>
<div className="module-left-pane">{this.renderSection()}</div>
@ -140,9 +137,6 @@ export class LeftPane extends React.Component<Props, State> {
private renderContactSection() {
const {
openConversationInternal,
friends,
sentFriendsRequest,
receivedFriendsRequest,
conversations,
searchResults,
searchTerm,
@ -150,23 +144,20 @@ export class LeftPane extends React.Component<Props, State> {
updateSearchTerm,
search,
clearSearch,
receivedFriendRequestCount,
contacts,
} = this.props;
return (
<LeftPaneContactSection
openConversationInternal={openConversationInternal}
conversations={conversations}
friends={friends}
contacts={contacts}
searchResults={searchResults}
searchTerm={searchTerm}
isSecondaryDevice={isSecondaryDevice}
updateSearchTerm={updateSearchTerm}
search={search}
clearSearch={clearSearch}
sentFriendsRequest={sentFriendsRequest}
receivedFriendsRequest={receivedFriendsRequest}
receivedFriendRequestCount={receivedFriendRequestCount}
/>
);
}

View file

@ -39,19 +39,16 @@ export class SearchResults extends React.Component<Props> {
openConversation,
searchTerm,
showStartNewConversation,
friends,
} = this.props;
const haveConversations = conversations && conversations.length;
const haveContacts = contacts && contacts.length;
const haveFriends = friends && friends.length;
const haveMessages = messages && messages.length;
const noResults =
!showStartNewConversation &&
!haveConversations &&
!haveContacts &&
!haveMessages &&
!haveFriends;
!haveMessages;
return (
<div className="module-search-results">
@ -75,8 +72,8 @@ export class SearchResults extends React.Component<Props> {
))}
</div>
) : null}
{haveFriends
? this.renderContacts(i18n('friendsHeader'), friends, true)
{haveContacts
? this.renderContacts(i18n('friendsHeader'), contacts, true)
: null}
{haveMessages ? (
@ -102,7 +99,7 @@ export class SearchResults extends React.Component<Props> {
private renderContacts(
header: string,
items: Array<ConversationListItemPropsType>,
friends?: boolean
contacts?: boolean
) {
const { i18n, openConversation } = this.props;
@ -112,7 +109,6 @@ export class SearchResults extends React.Component<Props> {
{items.map(contact => (
<ConversationListItem
key={contact.phoneNumber}
isFriend={friends}
{...contact}
onClick={openConversation}
i18n={i18n}

View file

@ -18,9 +18,8 @@ export class UserSearchResults extends React.Component<Props> {
public render() {
const { contacts, searchTerm } = this.props;
const friends = contacts.filter(contact => contact.isFriend);
const noResults = !friends || friends.length <= 0;
const noResults = !contacts || contacts.length <= 0;
return (
<div className="module-search-results">
@ -29,7 +28,7 @@ export class UserSearchResults extends React.Component<Props> {
{window.i18n('noSearchResults', [searchTerm])}
</div>
) : (
this.renderContacts(friends)
this.renderContacts(contacts)
)}
</div>
);
@ -38,12 +37,12 @@ export class UserSearchResults extends React.Component<Props> {
private renderContacts(items: Array<ConversationListItemPropsType>) {
return (
<div className="contacts-dropdown">
{items.map((friend, index) => this.renderFriend(friend, index))}
{items.map((contact, index) => this.renderContact(contact, index))}
</div>
);
}
private renderFriend(contact: ConversationListItemPropsType, index: Number) {
private renderContact(contact: ConversationListItemPropsType, index: Number) {
const { profileName, phoneNumber } = contact;
const { selectedContact } = this.props;

View file

@ -1,210 +0,0 @@
import React from 'react';
import classNames from 'classnames';
import { LocalizerType } from '../../types/Util';
import { MessageBody } from './MessageBody';
import { Timestamp } from './Timestamp';
interface Props {
text: string;
direction: 'incoming' | 'outgoing';
status: string;
friendStatus: 'pending' | 'accepted' | 'declined' | 'expired';
i18n: LocalizerType;
isBlocked: boolean;
timestamp: number;
onAccept: () => void;
onDecline: () => void;
onDeleteConversation: () => void;
onRetrySend: () => void;
onBlockUser: () => void;
onUnblockUser: () => void;
}
export class FriendRequest extends React.Component<Props> {
public getStringId() {
const { friendStatus } = this.props;
switch (friendStatus) {
case 'pending':
return 'friendRequestPending';
case 'accepted':
return 'friendRequestAccepted';
case 'declined':
return 'friendRequestDeclined';
case 'expired':
return 'friendRequestExpired';
default:
throw new Error(`Invalid friend request status: ${friendStatus}`);
}
}
public renderContents() {
const { direction, i18n, text } = this.props;
const id = this.getStringId();
return (
<div>
<div
className={`module-friend-request__title module-friend-request__title--${direction}`}
>
{i18n(id)}
</div>
<div dir="auto">
<MessageBody text={text || ''} i18n={i18n} />
</div>
</div>
);
}
public renderButtons() {
const {
i18n,
friendStatus,
direction,
status,
onAccept,
onDecline,
onDeleteConversation,
onRetrySend,
isBlocked,
onBlockUser,
onUnblockUser,
} = this.props;
if (direction === 'incoming') {
if (friendStatus === 'pending') {
return (
<div
className={classNames(
'module-message__metadata',
'module-friend-request__buttonContainer',
`module-friend-request__buttonContainer--${direction}`
)}
>
<button onClick={onAccept}>Accept</button>
<button onClick={onDecline}>Decline</button>
</div>
);
} else if (friendStatus === 'declined') {
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockUser : onBlockUser;
return (
<div
className={classNames(
'module-message__metadata',
'module-friend-request__buttonContainer',
`module-friend-request__buttonContainer--${direction}`
)}
>
<button onClick={onDeleteConversation}>Delete Conversation</button>
<button onClick={blockHandler}>{blockTitle}</button>
</div>
);
}
} else {
// Render the retry button if we errored
if (status === 'error' && friendStatus === 'pending') {
return (
<div
className={classNames(
'module-message__metadata',
'module-friend-request__buttonContainer',
`module-friend-request__buttonContainer--${direction}`
)}
>
<button onClick={onRetrySend}>Retry Send</button>
</div>
);
}
}
return null;
}
public renderError(isCorrectSide: boolean) {
const { status, direction } = this.props;
if (!isCorrectSide || status !== 'error') {
return null;
}
return (
<div className="module-message__error-container">
<div
className={classNames(
'module-message__error',
`module-message__error--${direction}`
)}
/>
</div>
);
}
// Renders 'sending', 'read' icons
public renderStatusIndicator() {
const { direction, status, i18n, text, timestamp } = this.props;
if (status === 'error') {
return null;
}
const withImageNoCaption = Boolean(!text);
return (
<div className="module-message__metadata">
<Timestamp
i18n={i18n}
timestamp={timestamp}
extended={true}
direction={direction}
withImageNoCaption={withImageNoCaption}
module="module-message__metadata__date"
/>
<span className="module-message__metadata__spacer" />
<div
className={classNames(
'module-message__metadata__status-icon',
`module-message__metadata__status-icon--${status}`
)}
/>
</div>
);
}
public render() {
const { direction } = this.props;
return (
<div className={'loki-message-wrapper'}>
<div
className={classNames(
`module-message module-message--${direction}`,
'module-message-friend-request'
)}
>
{this.renderError(direction === 'incoming')}
<div
className={classNames(
'module-message__container',
`module-message__container--${direction}`,
'module-message-friend-request__container'
)}
>
<div
className={classNames(
'module-message__text',
`module-message__text--${direction}`
)}
>
{this.renderContents()}
{this.renderStatusIndicator()}
{this.renderButtons()}
</div>
</div>
{this.renderError(direction === 'outgoing')}
</div>
</div>
);
}
}

View file

@ -21,7 +21,6 @@ interface Props {
selectedSection: SectionType;
conversations: Array<ConversationListItemPropsType> | undefined;
unreadMessageCount: number;
receivedFriendRequestCount: number;
}
export class ActionsPanel extends React.Component<Props, State> {
@ -130,7 +129,6 @@ export class ActionsPanel extends React.Component<Props, State> {
const {
selectedSection,
unreadMessageCount,
receivedFriendRequestCount,
} = this.props;
const isProfilePageSelected = selectedSection === SectionType.Profile;
@ -157,7 +155,6 @@ export class ActionsPanel extends React.Component<Props, State> {
type={SectionType.Contact}
isSelected={isContactPageSelected}
onSelect={this.handleSectionSelect}
notificationCount={receivedFriendRequestCount}
/>
<this.Section
type={SectionType.Settings}

View file

@ -28,10 +28,7 @@ export interface Props {
isSecondaryDevice: boolean;
conversations: Array<ConversationListItemPropsType>;
friends: Array<ConversationType>;
receivedFriendsRequest: Array<ConversationListItemPropsType>;
receivedFriendRequestCount: number;
sentFriendsRequest: Array<ConversationListItemPropsType>;
contacts: Array<ConversationType>;
searchResults?: SearchResultsProps;
@ -45,7 +42,6 @@ interface State {
showAddContactView: boolean;
selectedTab: number;
addContactRecipientID: string;
showFriendRequestsPopup: boolean;
pubKeyPasted: string;
}
@ -59,7 +55,6 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
selectedTab: 0,
addContactRecipientID: '',
pubKeyPasted: '',
showFriendRequestsPopup: false,
};
this.debouncedSearch = debounce(this.search.bind(this), 20);
@ -69,9 +64,6 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
this.handleRecipientSessionIDChanged = this.handleRecipientSessionIDChanged.bind(
this
);
this.handleToggleFriendRequestPopup = this.handleToggleFriendRequestPopup.bind(
this
);
}
public componentWillUnmount() {
@ -84,17 +76,15 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
}
public renderHeader(): JSX.Element | undefined {
const { receivedFriendRequestCount } = this.props;
// The feature "organize your friends as custom list" is not included in the first release
const labels = [window.i18n('contactsHeader') /*, window.i18n('lists')*/];
const labels = [window.i18n('contactsHeader')];
return LeftPane.RENDER_HEADER(
labels,
this.handleTabSelected,
undefined,
undefined,
this.handleToggleFriendRequestPopup,
receivedFriendRequestCount
undefined,
undefined
);
}
@ -117,37 +107,14 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
);
}
public renderRowFriendRequest = ({
index,
key,
style,
}: RowRendererParamsType): JSX.Element | undefined => {
const receivedFriendsRequest = this.props.receivedFriendsRequest;
const item = receivedFriendsRequest[index];
const onClick = this.props.openConversationInternal;
return (
<ConversationListItem
key={key}
style={style}
{...item}
i18n={window.i18n}
onClick={onClick}
/>
);
};
public renderRow = ({
index,
key,
style,
}: RowRendererParamsType): JSX.Element | undefined => {
const { sentFriendsRequest } = this.props;
const contacts = this.props.friends.filter(f => f.type === 'direct');
const friends = contacts.filter(c => c.isFriend);
const combined = [...sentFriendsRequest, ...friends];
const item = combined[index];
const contacts = this.getDirectContactsOnly();
const item = contacts[index];
return (
<ConversationListItem
@ -222,11 +189,6 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
}));
}
private handleToggleFriendRequestPopup() {
this.setState((prevState: { showFriendRequestsPopup: boolean }) => ({
showFriendRequestsPopup: !prevState.showFriendRequestsPopup,
}));
}
private handleOnAddContact() {
const sessionID = this.state.addContactRecipientID.trim();
@ -248,16 +210,9 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
}
private renderContacts() {
const { showFriendRequestsPopup } = this.state;
const hasReceivedFriendRequest =
this.props.receivedFriendsRequest.length > 0;
return (
<div className="left-pane-contact-content">
{this.renderList()}
{showFriendRequestsPopup &&
hasReceivedFriendRequest &&
this.renderFriendRequestPopup()}
{this.renderBottomButtons()}
</div>
);
@ -298,40 +253,13 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
);
}
private renderFriendRequestPopup() {
const frTitle = window.i18n('youHaveFriendRequestFrom');
const length = this.props.receivedFriendsRequest.length;
return (
<div className="module-left-pane__list-popup">
<div className="friend-request-title">{frTitle}</div>
<div className="module-left-pane__list-popup" key={0}>
<AutoSizer>
{({ height, width }) => (
<List
className="module-left-pane__virtual-list"
height={height}
rowCount={length}
rowHeight={64}
rowRenderer={this.renderRowFriendRequest}
width={width}
autoHeight={true}
/>
)}
</AutoSizer>
</div>
</div>
);
private getDirectContactsOnly() {
return this.props.contacts.filter(f => f.type === 'direct');
}
private renderList() {
const { sentFriendsRequest } = this.props;
const contacts = this.props.friends.filter(f => f.type === 'direct');
const friends = contacts.filter(c => c.isFriend);
const length = Number(sentFriendsRequest.length) + Number(friends.length);
const combined = [...sentFriendsRequest, ...friends];
const contacts = this.getDirectContactsOnly();
const length = Number(contacts.length);
const list = (
<div className="module-left-pane__list" key={0}>
@ -341,7 +269,6 @@ export class LeftPaneContactSection extends React.Component<Props, State> {
className="module-left-pane__virtual-list"
height={height}
rowCount={length}
combined={combined}
rowHeight={64}
rowRenderer={this.renderRow}
width={width}

View file

@ -119,7 +119,7 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
if (conversationList !== undefined) {
conversationList = conversationList.filter(
conversation =>
!conversation.isPendingFriendRequest && !conversation.isSecondary
!conversation.isSecondary
);
}
@ -154,16 +154,13 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
public renderList(): JSX.Element | Array<JSX.Element | null> {
const { openConversationInternal, searchResults } = this.props;
const friends =
(searchResults &&
searchResults.contacts.filter(contact => contact.isFriend)) ||
[];
const contacts = searchResults?.contacts || [];
if (searchResults) {
return (
<SearchResults
{...searchResults}
friends={friends}
contacts={contacts}
openConversation={openConversationInternal}
i18n={window.i18n}
/>

View file

@ -72,8 +72,7 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
return (
!conversation.isMe() &&
conversation.isPrivate() &&
!conversation.isSecondaryDevice() &&
conversation.isFriend()
!conversation.isSecondaryDevice()
);
});

1
ts/global.d.ts vendored
View file

@ -38,7 +38,6 @@ interface Window {
Session: any;
log: any;
i18n: any;
friends: any;
generateID: any;
storage: any;
pushToast: any;

View file

@ -18,7 +18,7 @@ export class SessionProtocol {
* This map should not be used directly, but instead through
* `updateSendSessionTimestamp()`, or `hasSendSessionRequest()`
*/
private static sentSessionsTimestamp: StringToNumberMap;
private static sentSessionsTimestamp: StringToNumberMap = {};
/**
* This map olds the processed session timestamps, i.e. when we received a session request and handled it.
@ -26,7 +26,7 @@ export class SessionProtocol {
* This map should not be used directly, but instead through
* `updateProcessedSessionTimestamp()`, `getProcessedSessionRequest()` or `hasProcessedSessionRequest()`
*/
private static processedSessionsTimestamp: StringToNumberMap;
private static processedSessionsTimestamp: StringToNumberMap = {};
/**
* This map olds the timestamp on which a sent session reset is triggered for a specific device.
@ -179,6 +179,8 @@ export class SessionProtocol {
if (!SessionProtocol.dbLoaded) {
const sentItem = await getItemById('sentSessionsTimestamp');
if (sentItem) {
// FIXME we must update the existing map with those items
// or empty the existing map, not create a new one
SessionProtocol.sentSessionsTimestamp = sentItem.value;
} else {
SessionProtocol.sentSessionsTimestamp = {};

View file

@ -133,7 +133,7 @@ export class MessageQueue implements MessageQueueInterface {
const messages = this.pendingMessageCache.getForDevice(device);
const isMediumGroup = GroupUtils.isMediumGroup(device);
const hasSession = SessionProtocol.hasSession(device);
const hasSession = await SessionProtocol.hasSession(device);
if (!isMediumGroup && !hasSession) {
await SessionProtocol.sendSessionRequestIfNeeded(device);

View file

@ -41,7 +41,7 @@ export async function getSyncContacts(): Promise<Array<any> | undefined> {
c =>
c.isPrivate() &&
!c.isOurLocalDevice() &&
c.isFriend() &&
!c.isBlocked() &&
!c.attributes.secondaryStatus
) || [];
@ -49,7 +49,7 @@ export async function getSyncContacts(): Promise<Array<any> | undefined> {
c =>
c.isPrivate() &&
!c.isOurLocalDevice() &&
c.isFriend() &&
!c.isBlocked() &&
c.attributes.secondaryStatus
);

View file

@ -52,12 +52,8 @@ export type ConversationType = {
mentionedUs: boolean;
isSelected: boolean;
isTyping: boolean;
isFriend?: boolean;
isSecondary?: boolean;
primaryDevice: string;
isPendingFriendRequest?: boolean;
hasReceivedFriendRequest?: boolean;
hasSentFriendRequest?: boolean;
};
export type ConversationLookupType = {
[key: string]: ConversationType;

View file

@ -97,9 +97,7 @@ export const _getLeftPaneLists = (
): {
conversations: Array<ConversationType>;
archivedConversations: Array<ConversationType>;
friends: Array<ConversationType>;
receivedFriendsRequest: Array<ConversationListItemPropsType>;
sentFriendsRequest: Array<ConversationListItemPropsType>;
contacts: Array<ConversationType>;
unreadCount: number;
} => {
const values = Object.values(lookup);
@ -107,9 +105,7 @@ export const _getLeftPaneLists = (
const conversations: Array<ConversationType> = [];
const archivedConversations: Array<ConversationType> = [];
const allFriends: Array<ConversationType> = [];
const allReceivedFriendsRequest: Array<ConversationListItemPropsType> = [];
const allSentFriendsRequest: Array<ConversationListItemPropsType> = [];
const allContacts: Array<ConversationType> = [];
const max = sorted.length;
let unreadCount = 0;
@ -124,34 +120,16 @@ export const _getLeftPaneLists = (
};
}
if (conversation.isFriend && conversation.activeAt !== undefined) {
allFriends.push(conversation);
if (conversation.activeAt !== undefined) {
allContacts.push(conversation);
}
if (conversation.hasReceivedFriendRequest) {
// Friend requests should always appear as coming from primary
const primaryConversation =
conversations.find(c => c.id === conversation.primaryDevice) ||
conversation;
primaryConversation.hasReceivedFriendRequest =
conversation.hasReceivedFriendRequest;
primaryConversation.isPendingFriendRequest =
conversation.isPendingFriendRequest;
allReceivedFriendsRequest.push(primaryConversation);
} else if (
if (
unreadCount < 9 &&
conversation.isFriend &&
conversation.unreadCount > 0
) {
unreadCount += conversation.unreadCount;
}
if (conversation.hasSentFriendRequest) {
if (!conversation.isFriend) {
if (!conversation.isSecondary) {
allSentFriendsRequest.push(conversation);
}
}
}
if (!conversation.activeAt) {
continue;
@ -191,20 +169,12 @@ export const _getLeftPaneLists = (
return filteredGroup as T;
};
const friends: Array<ConversationType> = filterToPrimary(allFriends);
const receivedFriendsRequest: Array<ConversationListItemPropsType> = filterToPrimary(
allReceivedFriendsRequest
);
const sentFriendsRequest: Array<ConversationListItemPropsType> = filterToPrimary(
allSentFriendsRequest
);
const contacts: Array<ConversationType> = filterToPrimary(allContacts);
return {
conversations,
archivedConversations,
friends,
receivedFriendsRequest,
sentFriendsRequest,
contacts,
unreadCount,
};
};

View file

@ -82,21 +82,6 @@ export const getSearchResults = createSelector(
return value;
})
),
friends: compact(
state.conversations.map(id => {
const value = lookup[id];
const friend = value && value.isFriend ? { ...value } : null;
if (friend && id === selectedConversation) {
return {
...friend,
isSelected: true,
};
}
return friend;
})
),
hideMessagesHeader: false,
messages: state.messages.map(message => {
if (message.id === selectedMessage) {

View file

@ -32,7 +32,6 @@ const mapStateToProps = (state: StateType) => {
showArchived: getShowArchived(state),
i18n: getIntl(state),
unreadMessageCount: leftPaneList.unreadCount,
receivedFriendRequestCount: leftPaneList.receivedFriendsRequest.length,
};
};