Merge pull request #47 from Mikunj/feature/blocking

Added blocking and unblocking
pull/51/head
sachaaaaa 6 years ago committed by GitHub
commit 7dcef4ee73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1619,5 +1619,15 @@
"example": "Bob"
}
}
},
"blockUser": {
"message": "Block user"
},
"unblockUser": {
"message": "Unblock user"
},
"settingsUnblockHeader": {
"message": "Blocked Users",
"description": "Shown in the settings page as the heading for the blocked user settings"
}
}

@ -614,6 +614,7 @@
<script type='text/javascript' src='js/registration.js'></script>
<script type='text/javascript' src='js/expire.js'></script>
<script type='text/javascript' src='js/conversation_controller.js'></script>
<script type='text/javascript' src='js/blocked_number_controller.js'></script>
<script type='text/javascript' src='js/views/react_wrapper_view.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script>

@ -8,6 +8,7 @@
storage,
textsecure,
Whisper,
BlockedNumberController
*/
// eslint-disable-next-line func-names
@ -422,6 +423,7 @@
try {
await ConversationController.load();
BlockedNumberController.load();
} catch (error) {
window.log.error(
'background.js: ConversationController failed to load:',

@ -0,0 +1,81 @@
/* global , Whisper, storage, ConversationController */
/* global textsecure: false */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
const blockedNumbers = new Whisper.BlockedNumberCollection();
window.getBlockedNumbers = () => blockedNumbers;
window.BlockedNumberController = {
getAll() {
try {
this.load();
} catch (e) {
window.log.warn(e);
}
return blockedNumbers;
},
reset() {
blockedNumbers.reset([]);
},
load() {
window.log.info('BlockedNumberController: starting initial fetch');
if (blockedNumbers.length) {
throw new Error('BlockedNumberController: Already loaded!');
}
if (!storage) {
throw new Error('BlockedNumberController: Could not load blocked numbers');
}
// Add the numbers to the collection
const numbers = storage.getBlockedNumbers();
blockedNumbers.add(
numbers.map(number => ({ number }))
);
},
block(number) {
const ourNumber = textsecure.storage.user.getNumber();
// Make sure we don't block ourselves
if (ourNumber === number) {
window.log.info('BlockedNumberController: Cannot block yourself!');
return;
}
storage.addBlockedNumber(number);
// Make sure we don't add duplicates
if (blockedNumbers.getNumber(number))
return;
blockedNumbers.add({ number });
},
unblock(number) {
storage.removeBlockedNumber(number);
// Make sure we don't add duplicates
const model = blockedNumbers.getNumber(number);
if (model) {
blockedNumbers.remove(model);
}
},
unblockAll() {
const all = blockedNumbers.models;
all.forEach(number => {
storage.removeBlockedNumber(number);
blockedNumbers.remove(number);
});
},
isBlocked(number) {
return storage.isBlocked(number);
},
};
})();

@ -1,9 +1,19 @@
/* global storage, _ */
/* global _: false */
/* global Backbone: false */
/* global BlockedNumberController: false */
/* global storage: false */
/* global Whisper: false */
/* eslint-disable more/no-then */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
const BLOCKED_NUMBERS_ID = 'blocked';
const BLOCKED_GROUPS_ID = 'blocked-groups';
@ -12,6 +22,7 @@
return _.include(numbers, number);
};
storage.getBlockedNumbers = () => storage.get(BLOCKED_NUMBERS_ID, []);
storage.addBlockedNumber = number => {
const numbers = storage.get(BLOCKED_NUMBERS_ID, []);
if (_.include(numbers, number)) {
@ -54,4 +65,29 @@
window.log.info(`removing group(${groupId} from blocked list`);
storage.put(BLOCKED_GROUPS_ID, _.without(groupIds, groupId));
};
Whisper.BlockedNumber = Backbone.Model.extend({
defaults() {
return {
number: '',
};
},
block() {
return BlockedNumberController.block(this.number);
},
unblock() {
return BlockedNumberController.unblock(this.number);
},
});
Whisper.BlockedNumberCollection = Backbone.Collection.extend({
model: Whisper.BlockedNumber,
comparator(m) {
return m.get('number');
},
getNumber(number) {
return this.models.find(m => m.get('number') === number);
},
});
})();

@ -146,7 +146,19 @@
isMe() {
return this.id === this.ourNumber;
},
isBlocked() {
return BlockedNumberController.isBlocked(this.id);
},
block() {
BlockedNumberController.block(this.id);
this.trigger('change');
this.messageCollection.forEach(m => m.trigger('change'));
},
unblock() {
BlockedNumberController.unblock(this.id);
this.trigger('change');
this.messageCollection.forEach(m => m.trigger('change'));
},
async cleanup() {
await window.Signal.Types.Conversation.deleteExternalFiles(
this.attributes,
@ -281,6 +293,7 @@
unreadCount: this.get('unreadCount') || 0,
isSelected: this.isSelected,
showFriendRequestIndicator: this.pendingFriendRequest,
isBlocked: this.isBlocked(),
lastMessage: {
status: this.lastMessageStatus,
text: this.lastMessage,

@ -331,15 +331,27 @@
window.Whisper.events.trigger('deleteConversation', conversation);
};
const onBlockUser = () => {
conversation.block();
this.trigger('change');
};
const onUnblockUser = () => {
conversation.unblock();
this.trigger('change');
};
return {
text: this.createNonBreakingLastSeparator(this.get('body')),
status: this.getMessagePropStatus(),
direction,
friendStatus,
isBlocked: conversation.isBlocked(),
onAccept,
onDecline,
onDeleteConversation,
onRetrySend: () => this.retrySend(),
onBlockUser,
onUnblockUser,
}
},
findContact(phoneNumber) {

@ -1,4 +1,4 @@
/* global $, Whisper */
/* global $, Whisper, storage */
$(document).on('keyup', e => {
'use strict';
@ -35,6 +35,8 @@ window.initialRequest = getInitialData();
window.initialRequest.then(data => {
'use strict';
storage.fetch();
window.initialData = data;
window.view = new Whisper.SettingsView();
window.view.$el.appendTo($body);

@ -1,5 +1,5 @@
/* global
dcodeIO, Backbone, _, libsignal, textsecure, ConversationController, stringObject */
dcodeIO, Backbone, _, libsignal, textsecure, ConversationController, stringObject, BlockedNumberController */
/* eslint-disable no-proto */
@ -955,7 +955,9 @@
await window.storage.fetch();
ConversationController.reset();
BlockedNumberController.reset();
await ConversationController.load();
BlockedNumberController.load();
},
async removeAllConfiguration() {
await window.Signal.Data.removeAllConfiguration();

@ -0,0 +1,92 @@
/* global BlockedNumberController: false */
/* global Whisper: false */
/* global storage: false */
/* global i18n: false */
/* eslint-disable no-new */
// eslint-disable-next-line func-names
(function() {
'use strict';
window.Whisper = window.Whisper || {};
Whisper.BlockedNumberView = Whisper.View.extend({
templateName: 'blockedUserSettings',
className: 'blockedUserSettings',
events: {
'click .unblock-button': 'onUnblock',
},
initialize() {
storage.onready(() => {
this.collection = BlockedNumberController.getAll();
this.listView = new Whisper.BlockedNumberListView({
collection: this.collection,
});
this.listView.render();
this.blockedUserSettings = this.$('.blocked-user-settings');
this.blockedUserSettings.prepend(this.listView.el);
});
},
render_attributes() {
return {
blockedHeader: i18n('settingsUnblockHeader'),
unblockMessage: i18n('unblockUser'),
};
},
onUnblock() {
const number = this.$('select option:selected').val();
if (!number) return;
if (BlockedNumberController.isBlocked(number)) {
BlockedNumberController.unblock(number);
window.onUnblockNumber(number);
this.listView.collection.remove(this.listView.collection.where({ number }));
}
},
});
Whisper.BlockedNumberListView = Whisper.View.extend({
tagName: 'select',
initialize(options) {
this.options = options || {};
this.listenTo(this.collection, 'add', this.addOne);
this.listenTo(this.collection, 'reset', this.addAll);
this.listenTo(this.collection, 'remove', this.addAll);
},
addOne(model) {
const number = model.get('number');
if (number) {
this.$el.append(`<option value="${number}">${this.truncate(number, 25)}</option>`);
}
},
addAll() {
this.$el.html('');
this.collection.each(this.addOne, this);
},
truncate(string, limit) {
// Make sure an element and number of items to truncate is provided
if (!string || !limit) return string;
// Get the inner content of the element
let content = string.trim();
// Convert the content into an array of words
// Remove any words above the limit
content = content.slice(0, limit);
// Convert the array of words back into a string
// If there's content to add after it, add it
if (string.length > limit)
content = `${content}...`;
return content;
},
render() {
this.addAll();
return this;
},
});
})();

@ -7,6 +7,7 @@
/* global Signal: false */
/* global storage: false */
/* global Whisper: false */
/* global BlockNumberConversation: false */
// eslint-disable-next-line func-names
(function() {
@ -170,6 +171,7 @@
isVerified: this.model.isVerified(),
isKeysPending: this.model.isKeyExchangeCompleted() === false,
isMe: this.model.isMe(),
isBlocked: this.model.isBlocked(),
isGroup: !this.model.isPrivate(),
expirationSettingName,
showBackButton: Boolean(this.panels && this.panels.length),
@ -200,6 +202,13 @@
this.resetPanel();
this.updateHeader();
},
onBlockUser: () => {
this.model.block();
},
onUnblockUser: () => {
this.model.unblock();
},
};
};
this.titleView = new Whisper.ReactWrapperView({

@ -117,6 +117,10 @@
value: window.initialData.mediaPermissions,
setFn: window.setMediaPermissions,
});
const blockedNumberView = new Whisper.BlockedNumberView().render();
this.$('.blocked-user-setting').append(blockedNumberView.el);
if (!window.initialData.isPrimary) {
const syncView = new SyncView().render();
this.$('.sync-setting').append(syncView.el);
@ -150,6 +154,7 @@
mediaPermissionsDescription: i18n('mediaPermissionsDescription'),
spellCheckHeader: i18n('spellCheck'),
spellCheckDescription: i18n('spellCheckDescription'),
blockedHeader: 'Blocked Users',
};
},
onClose() {

@ -930,6 +930,12 @@ ipc.on('set-media-permissions', (event, value) => {
event.sender.send('set-success-media-permissions', null);
});
ipc.on('on-unblock-number', (event, number) => {
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send('on-unblock-number', number);
}
});
installSettingsGetter('is-primary');
installSettingsGetter('sync-request');
installSettingsGetter('sync-time');

@ -77,6 +77,28 @@ window.setMediaPermissions = enabled =>
ipc.send('set-media-permissions', enabled);
window.getMediaPermissions = () => ipc.sendSync('get-media-permissions');
// Events for updating block number states across different windows.
// In this case we need these to update the blocked number
// collection on the main window from the settings window.
window.onUnblockNumber = number => ipc.send('on-unblock-number', number);
ipc.on('on-unblock-number', (event, number) => {
// Unblock the number
if (window.BlockedNumberController) {
window.BlockedNumberController.unblock(number);
}
// Update the conversation
if (window.ConversationController) {
try {
const conversation = window.ConversationController.get(number);
conversation.unblock();
} catch (e) {
window.log.info('IPC on unblock: failed to fetch conversation for number: ', number);
}
}
});
window.closeAbout = () => ipc.send('close-about');
window.updateTrayIcon = unreadCount =>

@ -34,6 +34,12 @@
</p>
</div>
</script>
<script type='text/x-tmpl-mustache' id='blockedUserSettings'>
<h3>{{ blockedHeader }}</h3>
<div class='blocked-user-settings'>
<button class='grey unblock-button'>{{ unblockMessage }}</button>
</div>
</script>
<script type='text/x-tmpl-mustache' id='settings'>
<div class='content'>
<a class='x close' alt='close settings' href='#'></a>
@ -109,10 +115,19 @@
<p>{{ clearDataExplanation }}</p>
</div>
</div>
<hr>
<div class='blocked-user-setting'>
</div>
</div>
</script>
<script type='text/javascript' src='js/components.js'></script>
<script type='text/javascript' src='js/storage.js'></script>
<script type='text/javascript' src='js/models/blockedNumbers.js'></script>
<script type='text/javascript' src='js/blocked_number_controller.js'></script>
<script type='text/javascript' src='js/views/whisper_view.js'></script>
<script type='text/javascript' src='js/views/list_view.js'></script>
<script type='text/javascript' src='js/views/blocked_number_view.js'></script>
<script type='text/javascript' src='js/views/settings_view.js'></script>
<script type='text/javascript' src='js/settings_start.js'></script>
</html>

@ -26,6 +26,11 @@ window.getAppInstance = () => config.appInstance;
window.closeSettings = () => ipcRenderer.send('close-settings');
// Events for updating block number states across different windows.
// In this case we need these to update the blocked number
// collection on the main window from the settings window.
window.onUnblockNumber = number => ipcRenderer.send('on-unblock-number', number);
window.getDeviceName = makeGetter('device-name');
window.getThemeSetting = makeGetter('theme-setting');

@ -1831,6 +1831,11 @@
border-left: 4px solid $color-conversation-indigo;
}
.module-conversation-list-item--is-blocked {
padding-left: 12px;
border-left: 4px solid $color-conversation-red;
}
.module-conversation-list-item--is-selected {
background-color: $color-gray-05;
}

@ -38,6 +38,42 @@
color: red;
}
}
.blocked-user-settings {
display: flex;
flex: 1;
flex-direction: row;
align-items: center;
}
.blocked-user-settings {
select {
flex: 1;
cursor: pointer;
font-size: 14px;
}
button {
line-height: 28px;
padding: 0 20px;
margin: auto;
margin-left: 20px;
}
}
.blocked-user-settings ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.wordwrap {
white-space: pre-wrap; /* CSS3 */
white-space: -moz-pre-wrap; /* Firefox */
white-space: -pre-wrap; /* Opera <7 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* IE */
}
.restart-needed {
margin-top: 1em;
}

@ -1269,6 +1269,10 @@ body.dark-theme {
border-left: 4px solid $color-conversation-indigo;
}
.module-conversation-list-item--is-blocked {
border-left: 4px solid $color-conversation-red;
}
.module-conversation-list-item--is-selected {
background-color: $color-dark-70;
}

@ -345,6 +345,7 @@
<script type="text/javascript" src="../js/models/conversations.js" data-cover></script>
<script type="text/javascript" src="../js/models/blockedNumbers.js" data-cover></script>
<script type="text/javascript" src="../js/conversation_controller.js" data-cover></script>
<script type='text/javascript' src='../js/blocked_number_controller.js'></script>
<script type="text/javascript" src="../js/keychange_listener.js" data-cover></script>
<script type='text/javascript' src='../js/expiring_messages.js' data-cover></script>
<script type='text/javascript' src='../js/notifications.js' data-cover></script>

@ -24,6 +24,7 @@ interface Props {
text: string;
};
showFriendRequestIndicator?: boolean;
isBlocked: boolean;
i18n: Localizer;
onClick?: () => void;
@ -157,7 +158,7 @@ export class ConversationListItem extends React.Component<Props> {
}
public render() {
const { unreadCount, onClick, isSelected, showFriendRequestIndicator } = this.props;
const { unreadCount, onClick, isSelected, showFriendRequestIndicator, isBlocked } = this.props;
return (
<div
@ -167,7 +168,8 @@ export class ConversationListItem extends React.Component<Props> {
'module-conversation-list-item',
unreadCount > 0 ? 'module-conversation-list-item--has-unread' : null,
isSelected ? 'module-conversation-list-item--is-selected' : null,
showFriendRequestIndicator ? 'module-conversation-list-item--has-friend-request' : null
showFriendRequestIndicator ? 'module-conversation-list-item--has-friend-request' : null,
isBlocked ? 'module-conversation-list-item--is-blocked' : null,
)}
>
{this.renderAvatar()}

@ -30,6 +30,7 @@ interface Props {
color: string;
avatarPath?: string;
isBlocked: boolean;
isMe: boolean;
isGroup: boolean;
expirationSettingName?: string;
@ -44,6 +45,9 @@ interface Props {
onShowAllMedia: () => void;
onShowGroupMembers: () => void;
onGoBack: () => void;
onBlockUser: () => void;
onUnblockUser: () => void;
}
export class ConversationHeader extends React.Component<Props> {
@ -182,6 +186,7 @@ export class ConversationHeader extends React.Component<Props> {
public renderMenu(triggerId: string) {
const {
i18n,
isBlocked,
isMe,
isGroup,
onDeleteMessages,
@ -191,10 +196,15 @@ export class ConversationHeader extends React.Component<Props> {
onShowGroupMembers,
onShowSafetyNumber,
timerOptions,
onBlockUser,
onUnblockUser,
} = this.props;
const disappearingTitle = i18n('disappearingMessages') as any;
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockUser : onBlockUser;
return (
<ContextMenu id={triggerId}>
<SubMenu title={disappearingTitle}>
@ -223,6 +233,10 @@ export class ConversationHeader extends React.Component<Props> {
{!isGroup ? (
<MenuItem onClick={onResetSession}>{i18n('resetSession')}</MenuItem>
) : null}
{/* Only show the block on other conversations */}
{!isMe ? (
<MenuItem onClick={blockHandler}>{blockTitle}</MenuItem>
) : null}
<MenuItem onClick={onDeleteMessages}>{i18n('deleteMessages')}</MenuItem>
</ContextMenu>
);

@ -10,10 +10,13 @@ interface Props {
status: string;
friendStatus: 'pending' | 'accepted' | 'declined';
i18n: Localizer;
isBlocked: boolean;
onAccept: () => void;
onDecline: () => void;
onDeleteConversation: () => void;
onRetrySend: () => void;
onBlockUser: () => void;
onUnblockUser: () => void;
}
export class FriendRequest extends React.Component<Props> {
@ -50,7 +53,7 @@ export class FriendRequest extends React.Component<Props> {
}
public renderButtons() {
const { friendStatus, direction, status, onAccept, onDecline, onDeleteConversation, onRetrySend } = this.props;
const { i18n, friendStatus, direction, status, onAccept, onDecline, onDeleteConversation, onRetrySend, isBlocked, onBlockUser, onUnblockUser } = this.props;
if (direction === 'incoming') {
if (friendStatus === 'pending') {
@ -67,6 +70,8 @@ export class FriendRequest extends React.Component<Props> {
</div>
);
} else if (friendStatus === 'declined') {
const blockTitle = isBlocked ? i18n('unblockUser') : i18n('blockUser');
const blockHandler = isBlocked ? onUnblockUser : onBlockUser;
return (
<div
className={classNames(
@ -76,6 +81,7 @@ export class FriendRequest extends React.Component<Props> {
)}
>
<button onClick={onDeleteConversation}>Delete Conversation</button>
<button onClick={blockHandler}>{blockTitle}</button>
</div>
);
}

Loading…
Cancel
Save