mirror of
https://github.com/oxen-io/session-desktop.git
synced 2023-12-14 02:12:57 +01:00
remove plenty of the friend logic
This commit is contained in:
parent
bccdc3cf34
commit
66de8d9648
36 changed files with 105 additions and 1285 deletions
|
@ -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',
|
||||
|
|
52
app/sql.js
52
app/sql.js
|
@ -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;
|
||||
`);
|
||||
|
|
|
@ -257,8 +257,6 @@
|
|||
promises.concat([
|
||||
conversation.updateProfileName(),
|
||||
conversation.updateProfileAvatar(),
|
||||
conversation.resetPendingSend(),
|
||||
conversation.setFriendRequestExpiryTimeout(),
|
||||
]);
|
||||
});
|
||||
await Promise.all(promises);
|
||||
|
|
6
js/models/conversation.d.ts
vendored
6
js/models/conversation.d.ts
vendored
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
})();
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:'));
|
||||
},
|
||||
|
||||
|
|
|
@ -565,7 +565,6 @@ describe('Backup', () => {
|
|||
const ommited = [
|
||||
'profileAvatar',
|
||||
'swarmNodes',
|
||||
'friendRequestStatus',
|
||||
'groupAdmins',
|
||||
'isKickedFromGroup',
|
||||
'unlockTimestamp',
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
|
|
|
@ -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
1
ts/global.d.ts
vendored
|
@ -38,7 +38,6 @@ interface Window {
|
|||
Session: any;
|
||||
log: any;
|
||||
i18n: any;
|
||||
friends: any;
|
||||
generateID: any;
|
||||
storage: any;
|
||||
pushToast: any;
|
||||
|
|
|
@ -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 = {};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -32,7 +32,6 @@ const mapStateToProps = (state: StateType) => {
|
|||
showArchived: getShowArchived(state),
|
||||
i18n: getIntl(state),
|
||||
unreadMessageCount: leftPaneList.unreadCount,
|
||||
receivedFriendRequestCount: leftPaneList.receivedFriendsRequest.length,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue