[NEW] Filter system messages per room (#1815)

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
Youssef Muhamad 2020-03-06 11:19:03 -03:00 committed by GitHub
parent ff807d705c
commit b9360217e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 250 additions and 58 deletions

View File

@ -20,7 +20,7 @@ const Chip = ({ item, onSelect, theme }) => (
>
<>
{item.imageUrl ? <Image style={styles.chipImage} source={{ uri: item.imageUrl }} /> : null}
<Text style={[styles.chipText, { color: themes[theme].titleText }]}>{textParser([item.text])}</Text>
<Text numberOfLines={1} style={[styles.chipText, { color: themes[theme].titleText }]}>{textParser([item.text])}</Text>
<CustomIcon name='cross' size={16} color={themes[theme].auxiliaryText} />
</>
</Touchable>

View File

@ -41,6 +41,12 @@ export const MultiSelect = React.memo(({
const [currentValue, setCurrentValue] = useState('');
const [showContent, setShowContent] = useState(false);
useEffect(() => {
if (values) {
select(values);
}
}, [values]);
useEffect(() => {
setOpen(showContent);
}, [showContent]);

View File

@ -34,6 +34,7 @@ export default StyleSheet.create({
},
item: {
height: 48,
maxWidth: '85%',
alignItems: 'center',
flexDirection: 'row'
},
@ -59,7 +60,7 @@ export default StyleSheet.create({
chips: {
flexDirection: 'row',
flexWrap: 'wrap',
marginRight: 16
marginRight: 50
},
chip: {
flexDirection: 'row',
@ -72,6 +73,7 @@ export default StyleSheet.create({
},
chipText: {
paddingHorizontal: 8,
flexShrink: 1,
...sharedStyles.textMedium,
fontSize: 14
},

View File

@ -210,6 +210,21 @@ export default {
Has_joined_the_channel: 'Has joined the channel',
Has_joined_the_conversation: 'Has joined the conversation',
Has_left_the_channel: 'Has left the channel',
Hide_System_Messages: 'Hide System Messages',
Hide_type_messages: 'Hide "{{type}}" messages',
Message_HideType_uj: 'User Join',
Message_HideType_ul: 'User Leave',
Message_HideType_ru: 'User Removed',
Message_HideType_au: 'User Added',
Message_HideType_mute_unmute: 'User Muted / Unmuted',
Message_HideType_r: 'Room Name Changed',
Message_HideType_ut: 'User Joined Conversation',
Message_HideType_wm: 'Welcome',
Message_HideType_rm: 'Message Removed',
Message_HideType_subscription_role_added: 'Was Set Role',
Message_HideType_subscription_role_removed: 'Role No Longer Defined',
Message_HideType_room_archived: 'Room Archived',
Message_HideType_room_unarchived: 'Room Unarchived',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP',
In_App_and_Desktop_Alert_info: 'Displays a banner at the top of the screen when app is open, and displays a notification on desktop',
@ -295,6 +310,7 @@ export default {
Only_authorized_users_can_write_new_messages: 'Only authorized users can write new messages',
Open_emoji_selector: 'Open emoji selector',
Open_Source_Communication: 'Open Source Communication',
Overwrites_the_server_configuration_and_use_room_config: 'Overwrites the server configuration and use room config',
Password: 'Password',
Permalink_copied_to_clipboard: 'Permalink copied to clipboard!',
Pin: 'Pin',
@ -454,6 +470,7 @@ export default {
Username_is_empty: 'Username is empty',
Username: 'Username',
Username_or_email: 'Username or email',
Uses_server_configuration: 'Uses server configuration',
Validating: 'Validating',
Verify_email_title: 'Registration Succeeded!',
Verify_email_desc: 'We have sent you an email to confirm your registration. If you do not receive an email shortly, please come back and try again.',

View File

@ -201,6 +201,21 @@ export default {
Has_joined_the_channel: 'Entrou no canal',
Has_joined_the_conversation: 'Entrou na conversa',
Has_left_the_channel: 'Saiu da conversa',
Hide_System_Messages: 'Esconder mensagens do sistema',
Hide_type_messages: 'Esconder mensagens de "{{type}}"',
Message_HideType_uj: 'Utilizador Entrou',
Message_HideType_ul: 'Utilizador Saiu',
Message_HideType_ru: 'Utilizador Removido',
Message_HideType_au: 'Utilizador adicionado',
Message_HideType_mute_unmute: 'Utilizador Silenciado',
Message_HideType_r: 'Nome da sala alterado',
Message_HideType_ut: 'Utilizador adicionado ao bate-papo',
Message_HideType_wm: 'Bem Vindo',
Message_HideType_rm: 'Mensagem Removida',
Message_HideType_subscription_role_added: 'Papel atribuído',
Message_HideType_subscription_role_removed: 'Papel removido',
Message_HideType_room_archived: 'Sala arquivada',
Message_HideType_room_unarchived: 'Sala desarquivada',
In_app: 'No app',
Invisible: 'Invisível',
Invite: 'Convidar',
@ -271,6 +286,7 @@ export default {
Only_authorized_users_can_write_new_messages: 'Somente usuários autorizados podem escrever novas mensagens',
Open_emoji_selector: 'Abrir seletor de emoji',
Open_Source_Communication: 'Comunicação Open Source',
Overwrites_the_server_configuration_and_use_room_config: 'Substituir a configuração do servidor e usar a configuração da sala',
Password: 'Senha',
Permalink_copied_to_clipboard: 'Link-permanente copiado para a área de transferência!',
Pin: 'Fixar',
@ -409,6 +425,7 @@ export default {
Username_is_empty: 'Usuário está vazio',
Username: 'Usuário',
Username_or_email: 'Usuário ou email',
Uses_server_configuration: 'Usar configuração do servidor',
Verify_email_title: 'Registrado com sucesso!',
Verify_email_desc: 'Nós lhe enviamos um e-mail para confirmar o seu registro. Se você não receber um e-mail em breve, por favor retorne e tente novamente.',
Video_call: 'Chamada de vídeo',

View File

@ -89,4 +89,6 @@ export default class Subscription extends Model {
@children('thread_messages') threadMessages;
@field('hide_unread_status') hideUnreadStatus;
@json('sys_mes', sanitizer) sysMes;
}

View File

@ -51,6 +51,17 @@ export default schemaMigrations({
]
})
]
},
{
toVersion: 6,
steps: [
addColumns({
table: 'subscriptions',
columns: [
{ name: 'sys_mes', type: 'string', isOptional: true }
]
})
]
}
]
});

View File

@ -1,7 +1,7 @@
import { appSchema, tableSchema } from '@nozbe/watermelondb';
export default appSchema({
version: 5,
version: 6,
tables: [
tableSchema({
name: 'subscriptions',
@ -39,7 +39,8 @@ export default appSchema({
{ name: 'jitsi_timeout', type: 'number', isOptional: true },
{ name: 'auto_translate', type: 'boolean', isOptional: true },
{ name: 'auto_translate_language', type: 'string' },
{ name: 'hide_unread_status', type: 'boolean', isOptional: true }
{ name: 'hide_unread_status', type: 'boolean', isOptional: true },
{ name: 'sys_mes', type: 'string', isOptional: true }
]
}),
tableSchema({

View File

@ -32,6 +32,7 @@ export const merge = (subscription, room) => {
} else {
subscription.muted = [];
}
subscription.sysMes = room.sysMes;
}
if (!subscription.name) {

42
app/utils/messageTypes.js Normal file
View File

@ -0,0 +1,42 @@
export const MessageTypeValues = [
{
value: 'uj',
text: 'Message_HideType_uj'
}, {
value: 'ul',
text: 'Message_HideType_ul'
}, {
value: 'ru',
text: 'Message_HideType_ru'
}, {
value: 'au',
text: 'Message_HideType_au'
}, {
value: 'mute_unmute',
text: 'Message_HideType_mute_unmute'
}, {
value: 'r',
text: 'Message_HideType_r'
}, {
value: 'ut',
text: 'Message_HideType_ut'
}, {
value: 'wm',
text: 'Message_HideType_wm'
}, {
value: 'rm',
text: 'Message_HideType_rm'
}, {
value: 'subscription_role_added',
text: 'Message_HideType_subscription_role_added'
}, {
value: 'subscription_role_removed',
text: 'Message_HideType_subscription_role_removed'
}, {
value: 'room_archived',
text: 'Message_HideType_room_archived'
}, {
value: 'room_unarchived',
text: 'Message_HideType_room_unarchived'
}
];

View File

@ -5,30 +5,17 @@ import PropTypes from 'prop-types';
import styles from './styles';
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
export default class SwitchContainer extends React.PureComponent {
static propTypes = {
value: PropTypes.bool,
disabled: PropTypes.bool,
leftLabelPrimary: PropTypes.string,
leftLabelSecondary: PropTypes.string,
rightLabelPrimary: PropTypes.string,
rightLabelSecondary: PropTypes.string,
onValueChange: PropTypes.func,
theme: PropTypes.string,
testID: PropTypes.string
}
render() {
const {
value, disabled, onValueChange, leftLabelPrimary, leftLabelSecondary, rightLabelPrimary, rightLabelSecondary, theme, testID
} = this.props;
return (
[
<View key='switch-container' style={styles.switchContainer}>
<View style={styles.switchLabelContainer}>
<Text style={[styles.switchLabelPrimary, { color: themes[theme].titleText }]}>{leftLabelPrimary}</Text>
<Text style={[styles.switchLabelSecondary, { color: themes[theme].titleText }]}>{leftLabelSecondary}</Text>
const SwitchContainer = React.memo(({
children, value, disabled, onValueChange, leftLabelPrimary, leftLabelSecondary, rightLabelPrimary, rightLabelSecondary, theme, testID, labelContainerStyle, leftLabelStyle
}) => (
<>
<View key='switch-container' style={[styles.switchContainer, children && styles.switchMargin]}>
{leftLabelPrimary && (
<View style={[styles.switchLabelContainer, labelContainerStyle]}>
<Text style={[styles.switchLabelPrimary, { color: themes[theme].titleText }, leftLabelStyle]}>{leftLabelPrimary}</Text>
<Text style={[styles.switchLabelSecondary, { color: themes[theme].titleText }, leftLabelStyle]}>{leftLabelSecondary}</Text>
</View>
)}
<Switch
style={styles.switch}
onValueChange={onValueChange}
@ -37,13 +24,31 @@ export default class SwitchContainer extends React.PureComponent {
trackColor={SWITCH_TRACK_COLOR}
testID={testID}
/>
<View style={styles.switchLabelContainer}>
<Text style={[styles.switchLabelPrimary, { color: themes[theme].titleText }]}>{rightLabelPrimary}</Text>
<Text style={[styles.switchLabelSecondary, { color: themes[theme].titleText }]}>{rightLabelSecondary}</Text>
{rightLabelPrimary && (
<View style={[styles.switchLabelContainer, labelContainerStyle]}>
<Text style={[styles.switchLabelPrimary, { color: themes[theme].titleText }, leftLabelStyle]}>{rightLabelPrimary}</Text>
<Text style={[styles.switchLabelSecondary, { color: themes[theme].titleText }, leftLabelStyle]}>{rightLabelSecondary}</Text>
</View>
</View>,
)}
</View>
{children}
<View key='switch-divider' style={[styles.divider, { borderColor: themes[theme].separatorColor }]} />
]
);
}
}
</>
));
SwitchContainer.propTypes = {
value: PropTypes.bool,
disabled: PropTypes.bool,
leftLabelPrimary: PropTypes.string,
leftLabelSecondary: PropTypes.string,
rightLabelPrimary: PropTypes.string,
rightLabelSecondary: PropTypes.string,
onValueChange: PropTypes.func,
theme: PropTypes.string,
testID: PropTypes.string,
labelContainerStyle: PropTypes.object,
leftLabelStyle: PropTypes.object,
children: PropTypes.any
};
export default SwitchContainer;

View File

@ -6,6 +6,9 @@ import {
import { connect } from 'react-redux';
import { SafeAreaView } from 'react-navigation';
import equal from 'deep-equal';
import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
import isEqual from 'lodash/isEqual';
import semver from 'semver';
import database from '../../lib/database';
import { deleteRoomInit as deleteRoomInitAction } from '../../actions/room';
@ -27,6 +30,8 @@ import StatusBar from '../../containers/StatusBar';
import { themedHeader } from '../../utils/navigation';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { MultiSelect } from '../../containers/UIKit/MultiSelect';
import { MessageTypeValues } from '../../utils/messageTypes';
const PERMISSION_SET_READONLY = 'set-readonly';
const PERMISSION_SET_REACT_WHEN_READONLY = 'set-react-when-readonly';
@ -52,6 +57,7 @@ class RoomInfoEditView extends React.Component {
static propTypes = {
navigation: PropTypes.object,
deleteRoomInit: PropTypes.func,
serverVersion: PropTypes.string,
theme: PropTypes.string
};
@ -70,7 +76,9 @@ class RoomInfoEditView extends React.Component {
t: false,
ro: false,
reactWhenReadOnly: false,
archived: false
archived: false,
systemMessages: [],
enableSysMes: false
};
this.loadRoom();
}
@ -117,7 +125,7 @@ class RoomInfoEditView extends React.Component {
init = (room) => {
const {
name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired
name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCodeRequired, sysMes
} = room;
// fake password just to user knows about it
this.randomValue = random(15);
@ -131,7 +139,9 @@ class RoomInfoEditView extends React.Component {
ro,
reactWhenReadOnly,
joinCode: joinCodeRequired ? this.randomValue : '',
archived: room.archived
archived: room.archived,
systemMessages: sysMes,
enableSysMes: sysMes && sysMes.length > 0
});
}
@ -148,7 +158,7 @@ class RoomInfoEditView extends React.Component {
formIsChanged = () => {
const {
room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode
room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages, enableSysMes
} = this.state;
const { joinCodeRequired } = room;
return !(room.name === name
@ -159,13 +169,15 @@ class RoomInfoEditView extends React.Component {
&& room.t === 'p' === t
&& room.ro === ro
&& room.reactWhenReadOnly === reactWhenReadOnly
&& isEqual(room.sysMes, systemMessages)
&& enableSysMes === (room.sysMes && room.sysMes.length > 0)
);
}
submit = async() => {
Keyboard.dismiss();
const {
room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode
room, name, description, topic, announcement, t, ro, reactWhenReadOnly, joinCode, systemMessages
} = this.state;
this.setState({ saving: true });
@ -210,6 +222,10 @@ class RoomInfoEditView extends React.Component {
params.reactWhenReadOnly = reactWhenReadOnly;
}
if (!isEqual(room.sysMes, systemMessages)) {
params.systemMessages = systemMessages;
}
// Join Code
if (this.randomValue !== joinCode) {
params.joinCode = joinCode;
@ -298,12 +314,34 @@ class RoomInfoEditView extends React.Component {
return (permissions[PERMISSION_ARCHIVE] || permissions[PERMISSION_UNARCHIVE]);
};
renderSystemMessages = () => {
const { systemMessages, enableSysMes } = this.state;
const { theme } = this.props;
if (!enableSysMes) {
return null;
}
return (
<MultiSelect
options={MessageTypeValues.map(m => ({ value: m.value, text: { text: I18n.t('Hide_type_messages', { type: I18n.t(m.text) }) } }))}
onChange={({ value }) => this.setState({ systemMessages: value })}
placeholder={{ text: I18n.t('Hide_System_Messages') }}
value={systemMessages}
context={BLOCK_CONTEXT.FORM}
multiselect
theme={theme}
/>
);
}
render() {
const {
name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived
name, nameError, description, topic, announcement, t, ro, reactWhenReadOnly, room, joinCode, saving, permissions, archived, enableSysMes
} = this.state;
const { theme } = this.props;
const { serverVersion, theme } = this.props;
const { dangerColor } = themes[theme];
return (
<KeyboardView
style={{ backgroundColor: themes[theme].backgroundColor }}
@ -408,6 +446,20 @@ class RoomInfoEditView extends React.Component {
]
: null
}
{serverVersion && !semver.lt(serverVersion, '3.0.0') ? (
<SwitchContainer
value={enableSysMes}
leftLabelPrimary={I18n.t('Hide_System_Messages')}
leftLabelSecondary={enableSysMes ? I18n.t('Overwrites_the_server_configuration_and_use_room_config') : I18n.t('Uses_server_configuration')}
theme={theme}
testID='room-info-edit-switch-system-messages'
onValueChange={value => this.setState(({ systemMessages }) => ({ enableSysMes: value, systemMessages: value ? systemMessages : [] }))}
labelContainerStyle={styles.hideSystemMessages}
leftLabelStyle={styles.systemMessagesLabel}
>
{this.renderSystemMessages()}
</SwitchContainer>
) : null}
<TouchableOpacity
style={[
styles.buttonContainer,
@ -497,8 +549,12 @@ class RoomInfoEditView extends React.Component {
}
}
const mapStateToProps = state => ({
serverVersion: state.server.version
});
const mapDispatchToProps = dispatch => ({
deleteRoomInit: (rid, t) => dispatch(deleteRoomInitAction(rid, t))
});
export default connect(null, mapDispatchToProps)(withTheme(RoomInfoEditView));
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RoomInfoEditView));

View File

@ -63,5 +63,14 @@ export default StyleSheet.create({
broadcast: {
...sharedStyles.textAlignCenter,
...sharedStyles.textSemibold
},
hideSystemMessages: {
alignItems: 'flex-start'
},
systemMessagesLabel: {
textAlign: 'left'
},
switchMargin: {
marginBottom: 16
}
});

View File

@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import orderBy from 'lodash/orderBy';
import { Q } from '@nozbe/watermelondb';
import moment from 'moment';
import isEqual from 'lodash/isEqual';
import styles from './styles';
import database from '../../lib/database';
@ -62,9 +63,15 @@ class List extends React.Component {
// eslint-disable-next-line react/sort-comp
async init() {
const { rid, tmid, hideSystemMessages = [] } = this.props;
const { rid, tmid } = this.props;
const db = database.active;
// handle servers with version < 3.0.0
let { hideSystemMessages = [] } = this.props;
if (!Array.isArray(hideSystemMessages)) {
hideSystemMessages = [];
}
if (tmid) {
try {
this.thread = await db.collections
@ -103,6 +110,12 @@ class List extends React.Component {
}
}
// eslint-disable-next-line react/sort-comp
reload = () => {
this.unsubscribeMessages();
this.init();
}
// this.state.loading works for this.onEndReached and RoomView.init
static getDerivedStateFromProps(props, state) {
if (props.loading !== state.loading) {
@ -115,7 +128,7 @@ class List extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
const { loading, end, refreshing } = this.state;
const { theme } = this.props;
const { hideSystemMessages, theme } = this.props;
if (theme !== nextProps.theme) {
return true;
}
@ -128,9 +141,19 @@ class List extends React.Component {
if (refreshing !== nextState.refreshing) {
return true;
}
if (!isEqual(hideSystemMessages, nextProps.hideSystemMessages)) {
return true;
}
return false;
}
componentDidUpdate(prevProps) {
const { hideSystemMessages } = this.props;
if (!isEqual(hideSystemMessages, prevProps.hideSystemMessages)) {
this.reload();
}
}
componentWillUnmount() {
this.unsubscribeMessages();
if (this.interaction && this.interaction.cancel) {

View File

@ -69,7 +69,7 @@ const stateAttrsUpdate = [
'reacting',
'showAnnouncementModal'
];
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement'];
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'muted', 'jitsiTimeout', 'announcement', 'sysMes'];
class RoomView extends React.Component {
static navigationOptions = ({ navigation, screenProps }) => {
@ -961,7 +961,7 @@ class RoomView extends React.Component {
const {
user, baseUrl, theme, navigation, Hide_System_Messages
} = this.props;
const { rid, t } = room;
const { rid, t, sysMes } = room;
return (
<SafeAreaView
@ -985,7 +985,7 @@ class RoomView extends React.Component {
renderRow={this.renderItem}
loading={loading}
navigation={navigation}
hideSystemMessages={Hide_System_Messages}
hideSystemMessages={sysMes || Hide_System_Messages}
/>
{this.renderAnnouncementModal()}
{this.renderFooter()}