Limit small private groups to 10 members

pull/565/head
Maxim Shishmarev 6 years ago
parent 0490e5c668
commit 0df5214979

@ -1989,6 +1989,12 @@
"Title for the dialog box used to connect to a new public server"
},
"createPrivateGroup": {
"message": "Create Private Group",
"description":
"Button action that the user can click to show a dialog for creating a new private group chat"
},
"seedViewTitle": {
"message":
"Please save the seed below in a safe location. They can be used to restore your account if you lose access or migrate to a new device.",
@ -2119,5 +2125,8 @@
"emptyGroupNameError": {
"message": "Group Name cannot be empty",
"description": "Error message displayed on empty group name"
},
"maxGroupMembersError": {
"message": "Max number of members for small group chats is: "
}
}

@ -196,6 +196,8 @@
? Whisper.ExpirationTimerOptions.getName(expireTimer || 0)
: null;
const members = this.model.get('members') || [];
return {
id: this.model.id,
name: this.model.getName(),
@ -213,7 +215,7 @@
isOnline: this.model.isOnline(),
isArchived: this.model.get('isArchived'),
isPublic: this.model.isPublic(),
members,
expirationSettingName,
showBackButton: Boolean(this.panels && this.panels.length),
timerOptions: Whisper.ExpirationTimerOptions.map(item => ({

@ -112,6 +112,8 @@
d => !_.some(existingMembers, x => x === d.id)
);
this.existingMembers = existingMembers;
this.$el.focus();
this.render();
},
@ -124,6 +126,7 @@
groupName: this.groupName,
okText: this.okText,
cancelText: this.cancelText,
existingMembers: this.existingMembers,
friendList: this.membersToShow,
onClose: this.close,
onSubmit: this.onSubmit,

@ -459,3 +459,6 @@ if (config.environment === 'test') {
window.shortenPubkey = pubkey => `(...${pubkey.substring(pubkey.length - 6)})`;
window.pubkeyPattern = /@[a-fA-F0-9]{64,66}\b/g;
// Limited due to the proof-of-work requirement
window.SMALL_GROUP_SIZE_LIMIT = 10;

@ -25,6 +25,10 @@
}
}
.member-preview {
margin-left: 10px;
}
.create-group-dialog {
.content {
max-width: 100% !important;

@ -27,8 +27,6 @@ export interface Props {
options: { regionCode: string }
) => void;
createNewGroup: () => void;
openConversationInternal: (id: string, messageId?: string) => void;
showArchivedConversations: () => void;
showInbox: () => void;
@ -265,12 +263,6 @@ export class LeftPane extends React.Component<Props, any> {
<div className="module-left-pane__header">
{showArchived ? this.renderArchivedHeader() : renderMainHeader()}
</div>
<input
className="create-group-button"
type="button"
value="Create Group"
onClick={this.props.createNewGroup}
/>
{this.renderList()}
</div>
);

@ -341,6 +341,13 @@ export class MainHeader extends React.Component<Props, any> {
trigger('showAddServerDialog');
},
},
{
id: 'createPrivateGroup',
name: i18n('createPrivateGroup'),
onClick: () => {
trigger('createNewGroup');
},
},
];
const passItem = (type: string) => ({

@ -31,6 +31,8 @@ interface Props {
isArchived: boolean;
isPublic: boolean;
members: Array<any>;
expirationSettingName?: string;
showBackButton: boolean;
timerOptions: Array<TimerOption>;
@ -262,9 +264,11 @@ export class ConversationHeader extends React.Component<Props> {
}
public render() {
const { id } = this.props;
const { id, isGroup, isPublic } = this.props;
const triggerId = `conversation-${id}`;
const isPrivateGroup = isGroup && !isPublic;
return (
<div className="module-conversation-header">
{this.renderBackButton()}
@ -272,6 +276,7 @@ export class ConversationHeader extends React.Component<Props> {
<div className="module-conversation-header__title-flex">
{this.renderAvatar()}
{this.renderTitle()}
{isPrivateGroup ? this.renderMemberCount() : null}
</div>
</div>
{this.renderExpirationLength()}
@ -281,6 +286,20 @@ export class ConversationHeader extends React.Component<Props> {
);
}
private renderMemberCount() {
const memberCount = this.props.members.length;
if (memberCount === 0) {
return null;
}
const wordForm = memberCount === 1 ? 'member' : 'members';
return (
<span className="member-preview">{`(${memberCount} ${wordForm})`}</span>
);
}
private renderPublicMenuItems() {
const {
i18n,

@ -6,6 +6,7 @@ declare global {
interface Window {
Lodash: any;
doCreateGroup: any;
SMALL_GROUP_SIZE_LIMIT: number;
}
}
@ -22,6 +23,7 @@ interface State {
friendList: Array<Contact>;
groupName: string;
errorDisplayed: boolean;
errorMessage: string;
}
export class CreateGroupDialog extends React.Component<Props, State> {
@ -56,6 +58,8 @@ export class CreateGroupDialog extends React.Component<Props, State> {
friendList: friends,
groupName: '',
errorDisplayed: false,
// if empty, the initial height is 0, which is not desirable
errorMessage: 'placeholder',
};
window.addEventListener('keyup', this.onKeyUp);
@ -67,7 +71,7 @@ export class CreateGroupDialog extends React.Component<Props, State> {
.map(d => d.id);
if (!this.state.groupName.trim()) {
this.onShowError();
this.onShowError(this.props.i18n('emptyGroupNameError'));
return;
}
@ -78,11 +82,12 @@ export class CreateGroupDialog extends React.Component<Props, State> {
}
public render() {
const titleText = this.props.titleText;
const checkMarkedCount = this.getMemberCount();
const titleText = `${this.props.titleText} (Members: ${checkMarkedCount})`;
const okText = this.props.okText;
const cancelText = this.props.cancelText;
const errorMsg = this.props.i18n('emptyGroupNameError');
const errorMessageClasses = classNames(
'error-message',
this.state.errorDisplayed ? 'error-shown' : 'error-faded'
@ -91,7 +96,7 @@ export class CreateGroupDialog extends React.Component<Props, State> {
return (
<div className="content">
<p className="titleText">{titleText}</p>
<p className={errorMessageClasses}>{errorMsg}</p>
<p className={errorMessageClasses}>{this.state.errorMessage}</p>
<input
type="text"
id="group-name"
@ -124,13 +129,14 @@ export class CreateGroupDialog extends React.Component<Props, State> {
);
}
private onShowError() {
private onShowError(msg: string) {
if (this.state.errorDisplayed) {
return;
}
this.setState({
errorDisplayed: true,
errorMessage: msg,
});
setTimeout(() => {
@ -164,6 +170,11 @@ export class CreateGroupDialog extends React.Component<Props, State> {
}
}
private getMemberCount() {
// Add 1 to include yourself
return this.state.friendList.filter(d => d.checkmarked).length + 1;
}
private closeDialog() {
window.removeEventListener('keyup', this.onKeyUp);
@ -171,15 +182,27 @@ export class CreateGroupDialog extends React.Component<Props, State> {
}
private onMemberClicked(selected: any) {
this.setState(state => {
const updatedFriends = this.state.friendList.map(member => {
if (member.id === selected.id) {
return { ...member, checkmarked: !member.checkmarked };
} else {
return member;
}
});
const updatedFriends = this.state.friendList.map(member => {
if (member.id === selected.id) {
return { ...member, checkmarked: !member.checkmarked };
} else {
return member;
}
});
if (
updatedFriends.filter(d => d.checkmarked).length >
window.SMALL_GROUP_SIZE_LIMIT - 1
) {
const msg = `${this.props.i18n('maxGroupMembersError')} ${
window.SMALL_GROUP_SIZE_LIMIT
}`;
this.onShowError(msg);
return;
}
this.setState(state => {
return {
...state,
friendList: updatedFriends,

@ -2,12 +2,20 @@ import React from 'react';
import classNames from 'classnames';
import { Contact, MemberList } from './MemberList';
declare global {
interface Window {
SMALL_GROUP_SIZE_LIMIT: number;
}
}
interface Props {
titleText: string;
groupName: string;
okText: string;
cancelText: string;
// friends not in the group
friendList: Array<any>;
existingMembers: Array<any>;
i18n: any;
onSubmit: any;
onClose: any;
@ -17,6 +25,7 @@ interface State {
friendList: Array<Contact>;
groupName: string;
errorDisplayed: boolean;
errorMessage: string;
}
export class UpdateGroupDialog extends React.Component<Props, State> {
@ -49,6 +58,7 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
friendList: friends,
groupName: this.props.groupName,
errorDisplayed: false,
errorMessage: 'placeholder',
};
window.addEventListener('keyup', this.onKeyUp);
@ -60,7 +70,7 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
.map(d => d.id);
if (!this.state.groupName.trim()) {
this.onShowError();
this.onShowError(this.props.i18n('emptyGroupNameError'));
return;
}
@ -71,7 +81,10 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
}
public render() {
const titleText = this.props.titleText;
const checkMarkedCount = this.getMemberCount();
const titleText = `${this.props.titleText} (Members: ${checkMarkedCount})`;
const okText = this.props.okText;
const cancelText = this.props.cancelText;
@ -80,7 +93,7 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
? 'no-friends'
: classNames('no-friends', 'hidden');
const errorMsg = this.props.i18n('emptyGroupNameError');
const errorMsg = this.state.errorMessage;
const errorMessageClasses = classNames(
'error-message',
this.state.errorDisplayed ? 'error-shown' : 'error-faded'
@ -123,13 +136,14 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
);
}
private onShowError() {
private onShowError(msg: string) {
if (this.state.errorDisplayed) {
return;
}
this.setState({
errorDisplayed: true,
errorMessage: msg,
});
setTimeout(() => {
@ -152,6 +166,13 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
}
}
private getMemberCount() {
return (
this.props.existingMembers.length +
this.state.friendList.filter(d => d.checkmarked).length
);
}
private closeDialog() {
window.removeEventListener('keyup', this.onKeyUp);
@ -159,15 +180,28 @@ export class UpdateGroupDialog extends React.Component<Props, State> {
}
private onMemberClicked(selected: any) {
this.setState(state => {
const updatedFriends = this.state.friendList.map(member => {
if (member.id === selected.id) {
return { ...member, checkmarked: !member.checkmarked };
} else {
return member;
}
});
const updatedFriends = this.state.friendList.map(member => {
if (member.id === selected.id) {
return { ...member, checkmarked: !member.checkmarked };
} else {
return member;
}
});
const newMemberCount =
this.props.existingMembers.length +
updatedFriends.filter(d => d.checkmarked).length;
if (newMemberCount > window.SMALL_GROUP_SIZE_LIMIT - 1) {
const msg = `${this.props.i18n('maxGroupMembersError')} ${
window.SMALL_GROUP_SIZE_LIMIT
}`;
this.onShowError(msg);
return;
}
this.setState(state => {
return {
...state,
friendList: updatedFriends,

@ -137,7 +137,6 @@ export const actions = {
openConversationExternal,
showInbox,
showArchivedConversations,
createNewGroup,
};
function conversationAdded(
@ -232,16 +231,6 @@ function showArchivedConversations() {
};
}
function createNewGroup() {
// Not sure how much of this is necessary:
trigger('createNewGroup');
return {
type: 'CREATE_NEW_GROUP',
payload: null,
};
}
// Reducer
function getEmptyState(): ConversationsStateType {

Loading…
Cancel
Save