Adding blocking of individual requests and syncing of block to devices. Added approval by replying to a message.

pull/2000/head
Warrick Corfe-Tan 3 years ago
parent 4ad14e4c5b
commit c3924f85a9

@ -443,5 +443,9 @@
"messageDeletedPlaceholder": "This message has been deleted",
"messageDeleted": "Message deleted",
"surveyTitle": "Take our Session Survey",
"goToOurSurvey": "Go to our survey"
"goToOurSurvey": "Go to our survey",
"blockAll": "Block All",
"messageRequests": "Message Requests",
"requestsSubtitle": "Pending Requests",
"requestsPlaceHolder": "No requests"
}

@ -835,6 +835,7 @@ const LOKI_SCHEMA_VERSIONS = [
updateToLokiSchemaVersion14,
updateToLokiSchemaVersion15,
updateToLokiSchemaVersion16,
updateToLokiSchemaVersion17,
];
function updateToLokiSchemaVersion1(currentVersion, db) {
@ -1228,6 +1229,23 @@ function updateToLokiSchemaVersion16(currentVersion, db) {
console.log(`updateToLokiSchemaVersion${targetVersion}: success!`);
}
function updateToLokiSchemaVersion17(currentVersion, db) {
const targetVersion = 17;
if (currentVersion >= targetVersion) {
return;
}
console.log(`updateToLokiSchemaVersion${targetVersion}: starting...`);
db.transaction(() => {
db.exec(`
ALTER TABLE ${CONVERSATIONS_TABLE} ADD COLUMN isApproved BOOLEAN;
`);
writeLokiSchemaVersion(targetVersion, db);
})();
console.log(`updateToLokiSchemaVersion${targetVersion}: success!`);
}
function writeLokiSchemaVersion(newVersion, db) {
db.prepare(
`INSERT INTO loki_schema(
@ -1604,8 +1622,8 @@ function updateConversation(data) {
profileName,
} = data;
// TODO: msgreq - remove
console.log({ usrData: data });
console.log({ usrDataTrace: console.trace() });
console.log('usrData@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@');
globalInstance
@ -1619,7 +1637,7 @@ function updateConversation(data) {
name = $name,
isApproved = $isApproved,
profileName = $profileName
WHERE id = $id;`
WHERE id = $id;`
)
.run({
id,

@ -170,7 +170,8 @@ message ConfigurationMessage {
required string name = 2;
optional string profilePicture = 3;
optional bytes profileKey = 4;
optional bool isApproved = 5;
optional bool isApproved = 5;
optional bool isBlocked = 6;
}
repeated ClosedGroup closedGroups = 1;

@ -31,6 +31,7 @@ import { Flex } from './basic/Flex';
import { SessionButton, SessionButtonColor } from './session/SessionButton';
import { getConversationById } from '../data/data';
import { syncConfigurationIfNeeded } from '../session/utils/syncUtils';
import { BlockedNumberController } from '../util';
// tslint:disable-next-line: no-empty-interface
export interface ConversationListItemProps extends ReduxConversationType {}
@ -284,13 +285,16 @@ const ConversationListItem = (props: Props) => {
);
/**
* deletes the conversation
* Removes conversation from requests list,
* adds ID to block list, syncs the block with linked devices.
*/
const handleConversationDecline = async () => {
// const convoToDecline = await getConversationById(conversationId);
// convoToDecline?.setIsApproved(false);
// await getConversationController().deleteContact(conversationId); // TODO: might be unnecessary
console.warn('decline');
const handleConversationBlock = async () => {
const convoToBlock = await getConversationById(conversationId);
if (!convoToBlock) {
window?.log?.error('Unable to find conversation to be blocked.');
}
await BlockedNumberController.block(convoToBlock?.id);
await syncConfigurationIfNeeded(true);
};
/**
@ -302,7 +306,6 @@ const ConversationListItem = (props: Props) => {
console.warn({ convoAfterSetIsApproved: conversationToApprove });
// TODO: Send sync message to other devices. Using config message
await syncConfigurationIfNeeded(true);
};
@ -364,10 +367,10 @@ const ConversationListItem = (props: Props) => {
justifyContent="flex-end"
>
<SessionButton
onClick={handleConversationDecline}
onClick={handleConversationBlock}
buttonColor={SessionButtonColor.Danger}
>
Decline
Block
</SessionButton>
<SessionButton
buttonColor={SessionButtonColor.Green}

@ -29,6 +29,7 @@ import { SNodeAPI } from '../../session/snode_api';
import { clearSearch, search, updateSearchTerm } from '../../state/ducks/search';
import _ from 'lodash';
import { MessageRequestsBanner } from './MessageRequestsBanner';
import { BlockedNumberController } from '../../util';
export interface Props {
searchTerm: string;
@ -85,8 +86,13 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
public renderRow = ({ index, key, style }: RowRendererParamsType): JSX.Element | null => {
const { conversations } = this.props;
const approvedConversations = conversations?.filter(c => Boolean(c.isApproved) === true);
if (!conversations || !approvedConversations) {
const conversationsToShow = conversations?.filter(async c => {
return (
Boolean(c.isApproved) === true &&
(await BlockedNumberController.isBlockedAsync(c.id)) === false
);
});
if (!conversations || !conversationsToShow) {
throw new Error('renderRow: Tried to render without conversations');
}
@ -95,8 +101,8 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
window.inboxStore?.getState().userConfig.messageRequests === true;
let conversation;
if (approvedConversations?.length) {
conversation = approvedConversations[index];
if (conversationsToShow?.length) {
conversation = conversationsToShow[index];
}
if (!conversation) {
@ -298,11 +304,8 @@ export class LeftPaneMessageSection extends React.Component<Props, State> {
this.handleToggleOverlay(undefined);
}}
onButtonClick={async () => {
// decline all convos
// close modal
// this.state.approvedConversations.map(async(convo) => {
// TODO: msgrequest iterate all convos and block
console.warn('Test');
// } )
}}
searchTerm={searchTerm}
searchResults={searchResults}

@ -83,10 +83,12 @@ export const CirclularIcon = (props: { iconType: SessionIconType; iconSize: Sess
export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => {
const { handleOnClick } = props;
const convos = useSelector(getLeftPaneLists).conversations;
const pendingRequestsCount = (convos.filter(c => c.isApproved !== true) || []).length;
const convos = useSelector(getLeftPaneLists).conversationRequests;
// const pendingRequestsCount = (
// convos.filter(c => Boolean(c.isApproved) === false && !Boolean(c.isBlocked)) || []
// ).length;
if (!pendingRequestsCount) {
if (!convos.length) {
return null;
}
@ -95,7 +97,7 @@ export const MessageRequestsBanner = (props: { handleOnClick: () => any }) => {
<CirclularIcon iconType="messageRequest" iconSize="medium" />
<StyledMessageRequestBannerHeader>Message Requests</StyledMessageRequestBannerHeader>
<StyledUnreadCounter>
<div>{pendingRequestsCount}</div>
<div>{convos.length || 0}</div>
</StyledUnreadCounter>
</StyledMessageRequestBanner>
);

@ -141,10 +141,10 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
placeholder = window.i18n('createClosedGroupPlaceholder');
break;
case SessionClosableOverlayType.MessageRequests:
title = 'Message Requests';
buttonText = 'Decline All';
subtitle = 'Pending Requests';
placeholder = 'placeholder';
title = window.i18n('messageRequests');
buttonText = window.i18n('blockAll');
subtitle = window.i18n('requestsSubtitle');
placeholder = window.i18n('requestsPlaceholder');
break;
default:
}
@ -291,16 +291,18 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
}
}
/**
* A request needs to be be unapproved and not blocked to be valid.
* @returns List of message request items
*/
const MessageRequestList = () => {
// get all conversations with (accepted / known)
const lists = useSelector(getLeftPaneLists);
const unapprovedConversations = lists?.conversations.filter(c => {
return !c.isApproved;
}) as Array<ConversationListItemProps>;
console.warn({ unapprovedConversationsListConstructor: unapprovedConversations });
// const validConversationRequests = lists?.conversations.filter(c => {
const validConversationRequests = lists?.conversationRequests;
console.warn({ unapprovedConversationsListConstructor: validConversationRequests });
return (
<div className="message-request-list__container">
{unapprovedConversations.map(conversation => {
{validConversationRequests.map(conversation => {
return <MessageRequestListItem conversation={conversation} />;
})}
</div>

@ -50,6 +50,7 @@ import { getDecryptedMediaUrl } from '../session/crypto/DecryptedAttachmentsMana
import { IMAGE_JPEG } from '../types/MIME';
import { UnsendMessage } from '../session/messages/outgoing/controlMessage/UnsendMessage';
import { getLatestTimestampOffset, networkDeleteMessages } from '../session/snode_api/SNodeAPI';
import { syncConfigurationIfNeeded } from '../session/utils/syncUtils';
export enum ConversationTypeEnum {
GROUP = 'group',
@ -715,9 +716,9 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
const sentAt = message.get('sent_at');
// TODO: for debugging
if (message.get('body')?.includes('unapprove')) {
console.warn('setting to unapprove');
// TODO: msgreq for debugging
const unapprove = message.get('body')?.includes('unapprove');
if (unapprove) {
await this.setIsApproved(false);
}
@ -740,6 +741,13 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
lokiProfile: UserUtils.getOurProfile(),
};
const updateApprovalNeeded =
!this.isApproved() && (this.isPrivate() || this.isMediumGroup() || this.isClosedGroup());
if (updateApprovalNeeded && !unapprove) {
this.setIsApproved(true);
await syncConfigurationIfNeeded(true);
}
if (this.isOpenGroupV2()) {
const chatMessageOpenGroupV2 = new OpenGroupVisibleMessage(chatMessageParams);
const roomInfos = this.toOpenGroupV2();
@ -1506,7 +1514,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
}
public isApproved() {
return this.get('isApproved');
return Boolean(this.get('isApproved'));
}
public getTitle() {

@ -11,6 +11,7 @@ import { getConversationController } from '../session/conversations';
import { UserUtils } from '../session/utils';
import { toHex } from '../session/utils/String';
import { configurationMessageReceived, trigger } from '../shims/events';
import { BlockedNumberController } from '../util';
import { removeFromCache } from './cache';
import { handleNewClosedGroup } from './closedGroups';
import { updateProfileOneAtATime } from './dataMessage';
@ -111,33 +112,42 @@ async function handleGroupsAndContactsFromConfigMessage(
}
}
if (configMessage.contacts?.length) {
await Promise.all(
configMessage.contacts.map(async c => {
try {
if (!c.publicKey) {
return;
}
const contactConvo = await getConversationController().getOrCreateAndWait(
toHex(c.publicKey),
ConversationTypeEnum.PRIVATE
);
const profile = {
displayName: c.name,
profilePictre: c.profilePicture,
};
// updateProfile will do a commit for us
contactConvo.set('active_at', _.toNumber(envelope.timestamp));
contactConvo.setIsApproved(Boolean(c.isApproved));
await updateProfileOneAtATime(contactConvo, profile, c.profileKey);
} catch (e) {
window?.log?.warn('failed to handle a new closed group from configuration message');
}
})
);
await Promise.all(configMessage.contacts.map(async c => handleContactReceived(c, envelope)));
}
}
const handleContactReceived = async (
contactReceived: SignalService.ConfigurationMessage.IContact,
envelope: EnvelopePlus
) => {
try {
if (!contactReceived.publicKey) {
return;
}
const contactConvo = await getConversationController().getOrCreateAndWait(
toHex(contactReceived.publicKey),
ConversationTypeEnum.PRIVATE
);
const profile = {
displayName: contactReceived.name,
profilePictre: contactReceived.profilePicture,
};
// updateProfile will do a commit for us
contactConvo.set('active_at', _.toNumber(envelope.timestamp));
contactConvo.setIsApproved(Boolean(contactReceived.isApproved));
if (contactReceived.isBlocked === true) {
await BlockedNumberController.block(contactConvo.id);
} else {
await BlockedNumberController.unblock(contactConvo.id);
}
await updateProfileOneAtATime(contactConvo, profile, contactReceived.profileKey);
} catch (e) {
window?.log?.warn('failed to handle a new closed group from configuration message');
}
};
export async function handleConfigurationMessage(
envelope: EnvelopePlus,
configurationMessage: SignalService.ConfigurationMessage

@ -262,6 +262,12 @@ export class ConversationController {
return Array.from(this.conversations.models);
}
public getConversationRequests(): Array<ConversationModel> {
return Array.from(this.conversations.models).filter(
conversation => conversation.isApproved() && !conversation.isBlocked
);
}
public unsafeDelete(convo: ConversationModel) {
this.conversations.remove(convo);
}

@ -94,6 +94,7 @@ export class ConfigurationMessageContact {
public profilePictureURL?: string;
public profileKey?: Uint8Array;
public isApproved?: boolean;
public isBlocked?: boolean;
public constructor({
publicKey,
@ -101,18 +102,21 @@ export class ConfigurationMessageContact {
profilePictureURL,
profileKey,
isApproved,
isBlocked,
}: {
publicKey: string;
displayName: string;
profilePictureURL?: string;
profileKey?: Uint8Array;
isApproved?: boolean;
isBlocked?: boolean;
}) {
this.publicKey = publicKey;
this.displayName = displayName;
this.profilePictureURL = profilePictureURL;
this.profileKey = profileKey;
this.isApproved = isApproved;
this.isBlocked = isBlocked;
// will throw if public key is invalid
PubKey.cast(publicKey);
@ -136,6 +140,7 @@ export class ConfigurationMessageContact {
profilePicture: this.profilePictureURL,
profileKey: this.profileKey,
isApproved: this.isApproved,
isBlocked: this.isBlocked,
});
}
}

@ -164,7 +164,7 @@ const getValidClosedGroups = async (convos: Array<ConversationModel>) => {
const getValidContacts = (convos: Array<ConversationModel>) => {
// Filter contacts
const contactsModels = convos.filter(
c => !!c.get('active_at') && c.getLokiProfile()?.displayName && c.isPrivate() && !c.isBlocked()
c => !!c.get('active_at') && c.getLokiProfile()?.displayName && c.isPrivate()
);
const contacts = contactsModels.map(c => {
@ -201,6 +201,7 @@ const getValidContacts = (convos: Array<ConversationModel>) => {
profilePictureURL: c.get('avatarPointer'),
profileKey: !profileKeyForContact?.length ? undefined : profileKeyForContact,
isApproved: c.isApproved(),
isBlocked: c.isBlocked(),
});
} catch (e) {
window?.log.warn('getValidContacts', e);

@ -275,6 +275,7 @@ export const _getLeftPaneLists = (
): {
conversations: Array<ReduxConversationType>;
contacts: Array<ReduxConversationType>;
conversationRequests: Array<ReduxConversationType>;
unreadCount: number;
} => {
const values = Object.values(lookup);
@ -282,6 +283,7 @@ export const _getLeftPaneLists = (
const conversations: Array<ReduxConversationType> = [];
const directConversations: Array<ReduxConversationType> = [];
const conversationRequests: Array<ReduxConversationType> = [];
let unreadCount = 0;
for (let conversation of sorted) {
@ -317,6 +319,10 @@ export const _getLeftPaneLists = (
directConversations.push(conversation);
}
if (!conversation.isApproved && !conversation.isBlocked) {
conversationRequests.push(conversation);
}
if (
unreadCount < 9 &&
conversation.unreadCount &&
@ -326,12 +332,15 @@ export const _getLeftPaneLists = (
unreadCount += conversation.unreadCount;
}
conversations.push(conversation);
if (conversation.isApproved) {
conversations.push(conversation);
}
}
return {
conversations,
contacts: directConversations,
conversationRequests,
unreadCount,
};
};

Loading…
Cancel
Save