[FIX] App not prompting join code for password protected channels (#2514)
* Adding joinCode parameter Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com> Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br> * Insert join code input Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> * Add joinCode field on db Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> * Add label i18 pt-br and en-us Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> * Add insert join code text Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> * Fix atribute name Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> * Add join text Signed-off-by: Vitor.Leal <vitor_leal2201@hotmail.com> Co-authored-by: Daniel Maike <danmke@hotmail.com> Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br> * Fix attributes joinCode, joinCodeRequired and pass attribute param in navigation Signed-off-by: Daniel Maike <danmke@hotmail.com> Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com> * Fixing attribute joinCodeRequired pass to goRoom Signed-off-by: Daniel Maike <danmke@hotmail.com> * Changed textinput style Signed-off-by: Daniel Maike <danmke@hotmail.com> Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com> * Delete not necessary attribute Signed-off-by: Daniel Maike <danmke@hotmail.com> * Fixing input style Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com> * Undo unncessary changes * use a join code modal * tests: e2e tests to join protected channel * fix: undo unnecessary change * tests: cancel join code * Remove some tests * Minor fixes Co-authored-by: Vitor Leal <vitor_leal2201@hotmail.com> Co-authored-by: Fernando Aguilar <fernando.aguilar@hotmail.com.br> Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com> Co-authored-by: youssef-md <emaildeyoussefmuhamad@gmail.com> Co-authored-by: Diego Mello <diegolmello@gmail.com>
This commit is contained in:
parent
06521d17cc
commit
13985cf724
|
@ -281,6 +281,8 @@ export default {
|
||||||
Invite_Link: 'Invite Link',
|
Invite_Link: 'Invite Link',
|
||||||
Invite_users: 'Invite users',
|
Invite_users: 'Invite users',
|
||||||
Join: 'Join',
|
Join: 'Join',
|
||||||
|
Join_Code: 'Join Code',
|
||||||
|
Insert_Join_Code: 'Insert Join Code',
|
||||||
Join_our_open_workspace: 'Join our open workspace',
|
Join_our_open_workspace: 'Join our open workspace',
|
||||||
Join_your_workspace: 'Join your workspace',
|
Join_your_workspace: 'Join your workspace',
|
||||||
Just_invited_people_can_access_this_channel: 'Just invited people can access this channel',
|
Just_invited_people_can_access_this_channel: 'Just invited people can access this channel',
|
||||||
|
|
|
@ -270,6 +270,8 @@ export default {
|
||||||
Invite_Link: 'Link de Convite',
|
Invite_Link: 'Link de Convite',
|
||||||
Invite_users: 'Convidar usuários',
|
Invite_users: 'Convidar usuários',
|
||||||
Join: 'Entrar',
|
Join: 'Entrar',
|
||||||
|
Join_Code: 'Insira o Código da Sala',
|
||||||
|
Insert_Join_Code: 'Insira o código para entrar na sala',
|
||||||
Join_our_open_workspace: 'Entra na nossa workspace pública',
|
Join_our_open_workspace: 'Entra na nossa workspace pública',
|
||||||
Join_your_workspace: 'Entre na sua workspace',
|
Join_your_workspace: 'Entre na sua workspace',
|
||||||
Just_invited_people_can_access_this_channel: 'Apenas as pessoas convidadas podem acessar este canal',
|
Just_invited_people_can_access_this_channel: 'Apenas as pessoas convidadas podem acessar este canal',
|
||||||
|
|
|
@ -705,13 +705,13 @@ const RocketChat = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
joinRoom(roomId, type) {
|
joinRoom(roomId, joinCode, type) {
|
||||||
// TODO: join code
|
// TODO: join code
|
||||||
// RC 0.48.0
|
// RC 0.48.0
|
||||||
if (type === 'p') {
|
if (type === 'p') {
|
||||||
return this.methodCallWrapper('joinRoom', roomId);
|
return this.methodCallWrapper('joinRoom', roomId);
|
||||||
}
|
}
|
||||||
return this.post('channels.join', { roomId });
|
return this.post('channels.join', { roomId, joinCode });
|
||||||
},
|
},
|
||||||
triggerBlockAction,
|
triggerBlockAction,
|
||||||
triggerSubmitView,
|
triggerSubmitView,
|
||||||
|
|
|
@ -149,8 +149,9 @@ class DirectoryView extends React.Component {
|
||||||
this.goRoom({ rid: result.room._id, name: item.username, t: 'd' });
|
this.goRoom({ rid: result.room._id, name: item.username, t: 'd' });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const { room } = await RocketChat.getRoomInfo(item._id);
|
||||||
this.goRoom({
|
this.goRoom({
|
||||||
rid: item._id, name: item.name, t: 'c', search: true
|
rid: item._id, name: item.name, joinCodeRequired: room.joinCodeRequired, t: 'c', search: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,140 @@
|
||||||
|
import React, {
|
||||||
|
useState,
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle
|
||||||
|
} from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
StyleSheet,
|
||||||
|
InteractionManager
|
||||||
|
} from 'react-native';
|
||||||
|
import Modal from 'react-native-modal';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import I18n from '../../i18n';
|
||||||
|
import Button from '../../containers/Button';
|
||||||
|
import TextInput from '../../containers/TextInput';
|
||||||
|
import RocketChat from '../../lib/rocketchat';
|
||||||
|
import sharedStyles from '../Styles';
|
||||||
|
|
||||||
|
import { themes } from '../../constants/colors';
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
padding: 16,
|
||||||
|
width: '100%',
|
||||||
|
borderRadius: 4
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 16,
|
||||||
|
paddingBottom: 8,
|
||||||
|
...sharedStyles.textBold,
|
||||||
|
...sharedStyles.textAlignCenter
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
minWidth: 96,
|
||||||
|
marginBottom: 0
|
||||||
|
},
|
||||||
|
buttonContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between'
|
||||||
|
},
|
||||||
|
tablet: {
|
||||||
|
height: undefined
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const JoinCode = React.memo(forwardRef(({
|
||||||
|
rid,
|
||||||
|
t,
|
||||||
|
onJoin,
|
||||||
|
isMasterDetail,
|
||||||
|
theme
|
||||||
|
}, ref) => {
|
||||||
|
const [visible, setVisible] = useState(false);
|
||||||
|
const [error, setError] = useState(false);
|
||||||
|
const [code, setCode] = useState('');
|
||||||
|
|
||||||
|
const show = () => setVisible(true);
|
||||||
|
|
||||||
|
const hide = () => setVisible(false);
|
||||||
|
|
||||||
|
const joinRoom = async() => {
|
||||||
|
try {
|
||||||
|
await RocketChat.joinRoom(rid, code, t);
|
||||||
|
onJoin();
|
||||||
|
hide();
|
||||||
|
} catch (e) {
|
||||||
|
setError(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({ show }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
transparent
|
||||||
|
avoidKeyboard
|
||||||
|
useNativeDriver
|
||||||
|
isVisible={visible}
|
||||||
|
hideModalContentWhileAnimating
|
||||||
|
>
|
||||||
|
<View style={styles.container} testID='join-code'>
|
||||||
|
<View style={[styles.content, isMasterDetail && [sharedStyles.modalFormSheet, styles.tablet], { backgroundColor: themes[theme].backgroundColor }]}>
|
||||||
|
<Text style={[styles.title, { color: themes[theme].titleText }]}>{I18n.t('Insert_Join_Code')}</Text>
|
||||||
|
<TextInput
|
||||||
|
value={code}
|
||||||
|
theme={theme}
|
||||||
|
inputRef={e => InteractionManager.runAfterInteractions(() => e?.getNativeRef()?.focus())}
|
||||||
|
returnKeyType='send'
|
||||||
|
autoCapitalize='none'
|
||||||
|
onChangeText={setCode}
|
||||||
|
onSubmitEditing={joinRoom}
|
||||||
|
placeholder={I18n.t('Join_Code')}
|
||||||
|
secureTextEntry
|
||||||
|
error={error && { error: 'error-code-invalid', reason: I18n.t('Code_or_password_invalid') }}
|
||||||
|
testID='join-code-input'
|
||||||
|
/>
|
||||||
|
<View style={styles.buttonContainer}>
|
||||||
|
<Button
|
||||||
|
title={I18n.t('Cancel')}
|
||||||
|
type='secondary'
|
||||||
|
style={styles.button}
|
||||||
|
backgroundColor={themes[theme].chatComponentBackground}
|
||||||
|
theme={theme}
|
||||||
|
testID='join-code-cancel'
|
||||||
|
onPress={hide}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title={I18n.t('Join')}
|
||||||
|
type='primary'
|
||||||
|
style={styles.button}
|
||||||
|
theme={theme}
|
||||||
|
testID='join-code-submit'
|
||||||
|
onPress={joinRoom}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
JoinCode.propTypes = {
|
||||||
|
rid: PropTypes.string,
|
||||||
|
t: PropTypes.string,
|
||||||
|
onJoin: PropTypes.func,
|
||||||
|
isMasterDetail: PropTypes.bool,
|
||||||
|
theme: PropTypes.string
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
isMasterDetail: state.app.isMasterDetail
|
||||||
|
});
|
||||||
|
export default connect(mapStateToProps, null, null, { forwardRef: true })(JoinCode);
|
|
@ -24,6 +24,7 @@ import MessageErrorActions from '../../containers/MessageErrorActions';
|
||||||
import MessageBox from '../../containers/MessageBox';
|
import MessageBox from '../../containers/MessageBox';
|
||||||
import ReactionPicker from './ReactionPicker';
|
import ReactionPicker from './ReactionPicker';
|
||||||
import UploadProgress from './UploadProgress';
|
import UploadProgress from './UploadProgress';
|
||||||
|
import JoinCode from './JoinCode';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import log, { logEvent, events } from '../../utils/log';
|
import log, { logEvent, events } from '../../utils/log';
|
||||||
import EventEmitter from '../../utils/events';
|
import EventEmitter from '../../utils/events';
|
||||||
|
@ -73,7 +74,7 @@ const stateAttrsUpdate = [
|
||||||
'readOnly',
|
'readOnly',
|
||||||
'member'
|
'member'
|
||||||
];
|
];
|
||||||
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'tunread', 'muted', 'ignored', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles', 'bannerClosed', 'visitor'];
|
const roomAttrsUpdate = ['f', 'ro', 'blocked', 'blocker', 'archived', 'tunread', 'muted', 'ignored', 'jitsiTimeout', 'announcement', 'sysMes', 'topic', 'name', 'fname', 'roles', 'bannerClosed', 'visitor', 'joinCodeRequired'];
|
||||||
|
|
||||||
class RoomView extends React.Component {
|
class RoomView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -152,6 +153,7 @@ class RoomView extends React.Component {
|
||||||
|
|
||||||
this.messagebox = React.createRef();
|
this.messagebox = React.createRef();
|
||||||
this.list = React.createRef();
|
this.list = React.createRef();
|
||||||
|
this.joinCode = React.createRef();
|
||||||
this.mounted = false;
|
this.mounted = false;
|
||||||
if (this.rid) {
|
if (this.rid) {
|
||||||
this.sub = new RoomClass(this.rid);
|
this.sub = new RoomClass(this.rid);
|
||||||
|
@ -711,6 +713,12 @@ class RoomView extends React.Component {
|
||||||
|
|
||||||
setLastOpen = lastOpen => this.setState({ lastOpen });
|
setLastOpen = lastOpen => this.setState({ lastOpen });
|
||||||
|
|
||||||
|
onJoin = () => {
|
||||||
|
this.internalSetState({
|
||||||
|
joined: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
joinRoom = async() => {
|
joinRoom = async() => {
|
||||||
logEvent(events.ROOM_JOIN);
|
logEvent(events.ROOM_JOIN);
|
||||||
try {
|
try {
|
||||||
|
@ -719,11 +727,14 @@ class RoomView extends React.Component {
|
||||||
if (this.isOmnichannel) {
|
if (this.isOmnichannel) {
|
||||||
await takeInquiry(room._id);
|
await takeInquiry(room._id);
|
||||||
} else {
|
} else {
|
||||||
await RocketChat.joinRoom(this.rid, this.t);
|
const { joinCodeRequired } = room;
|
||||||
|
if (joinCodeRequired) {
|
||||||
|
this.joinCode.current?.show();
|
||||||
|
} else {
|
||||||
|
await RocketChat.joinRoom(this.rid, null, this.t);
|
||||||
|
this.onJoin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.internalSetState({
|
|
||||||
joined: true
|
|
||||||
});
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
|
@ -1086,6 +1097,13 @@ class RoomView extends React.Component {
|
||||||
onClose={this.onCloseReactionsModal}
|
onClose={this.onCloseReactionsModal}
|
||||||
getCustomEmoji={this.getCustomEmoji}
|
getCustomEmoji={this.getCustomEmoji}
|
||||||
/>
|
/>
|
||||||
|
<JoinCode
|
||||||
|
ref={this.joinCode}
|
||||||
|
onJoin={this.onJoin}
|
||||||
|
rid={rid}
|
||||||
|
t={t}
|
||||||
|
theme={theme}
|
||||||
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
||||||
channels: {
|
channels: {
|
||||||
detoxpublic: {
|
detoxpublic: {
|
||||||
name: 'detox-public'
|
name: 'detox-public'
|
||||||
|
},
|
||||||
|
detoxpublicprotected: {
|
||||||
|
name: 'detox-public-protected',
|
||||||
|
joinCode: '123'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
||||||
channels: {
|
channels: {
|
||||||
detoxpublic: {
|
detoxpublic: {
|
||||||
name: 'detox-public'
|
name: 'detox-public'
|
||||||
|
},
|
||||||
|
detoxpublicprotected: {
|
||||||
|
name: 'detox-public-protected',
|
||||||
|
joinCode: '123'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
||||||
channels: {
|
channels: {
|
||||||
detoxpublic: {
|
detoxpublic: {
|
||||||
name: 'detox-public'
|
name: 'detox-public'
|
||||||
|
},
|
||||||
|
detoxpublicprotected: {
|
||||||
|
name: 'detox-public-protected',
|
||||||
|
joinCode: '123'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
groups: {
|
groups: {
|
||||||
|
|
|
@ -40,12 +40,14 @@ const createUser = async (username, password, name, email) => {
|
||||||
const createChannelIfNotExists = async (channelname) => {
|
const createChannelIfNotExists = async (channelname) => {
|
||||||
console.log(`Creating public channel ${channelname}`)
|
console.log(`Creating public channel ${channelname}`)
|
||||||
try {
|
try {
|
||||||
await rocketchat.post('channels.create', {
|
const room = await rocketchat.post('channels.create', {
|
||||||
"name": channelname
|
"name": channelname
|
||||||
})
|
})
|
||||||
|
return room
|
||||||
} catch (createError) {
|
} catch (createError) {
|
||||||
try { //Maybe it exists already?
|
try { //Maybe it exists already?
|
||||||
await rocketchat.get(`channels.info?roomName=${channelname}`)
|
const room = rocketchat.get(`channels.info?roomName=${channelname}`)
|
||||||
|
return room
|
||||||
} catch (infoError) {
|
} catch (infoError) {
|
||||||
console.log(JSON.stringify(createError))
|
console.log(JSON.stringify(createError))
|
||||||
console.log(JSON.stringify(infoError))
|
console.log(JSON.stringify(infoError))
|
||||||
|
@ -71,6 +73,24 @@ const createGroupIfNotExists = async (groupname) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changeChannelJoinCode = async (roomId, joinCode) => {
|
||||||
|
console.log(`Changing channel Join Code ${roomId}`)
|
||||||
|
try {
|
||||||
|
await rocketchat.post('method.call/saveRoomSettings', {
|
||||||
|
message: JSON.stringify({
|
||||||
|
method: 'saveRoomSettings',
|
||||||
|
params: [
|
||||||
|
roomId,
|
||||||
|
{ joinCode }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (createError) {
|
||||||
|
console.log(JSON.stringify(createError))
|
||||||
|
throw "Failed to create protected channel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const sendMessage = async (user, groupname, msg) => {
|
const sendMessage = async (user, groupname, msg) => {
|
||||||
console.log(`Sending message to ${groupname}`)
|
console.log(`Sending message to ${groupname}`)
|
||||||
try {
|
try {
|
||||||
|
@ -96,7 +116,11 @@ const setup = async () => {
|
||||||
for (var channelKey in data.channels) {
|
for (var channelKey in data.channels) {
|
||||||
if (data.channels.hasOwnProperty(channelKey)) {
|
if (data.channels.hasOwnProperty(channelKey)) {
|
||||||
const channel = data.channels[channelKey]
|
const channel = data.channels[channelKey]
|
||||||
await createChannelIfNotExists(channel.name)
|
const { data: { channel: { _id } } } = await createChannelIfNotExists(channel.name)
|
||||||
|
|
||||||
|
if (channel.joinCode) {
|
||||||
|
await changeChannelJoinCode(_id, channel.joinCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
const {
|
||||||
|
device, expect, element, by, waitFor
|
||||||
|
} = require('detox');
|
||||||
|
const data = require('../../data');
|
||||||
|
const { navigateToLogin, login, mockMessage, tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||||
|
|
||||||
|
const testuser = data.users.regular
|
||||||
|
const room = data.channels.detoxpublicprotected.name
|
||||||
|
const joinCode = data.channels.detoxpublicprotected.joinCode
|
||||||
|
|
||||||
|
async function navigateToRoom() {
|
||||||
|
await searchRoom(room);
|
||||||
|
await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
|
||||||
|
await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
|
||||||
|
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function navigateToRoomActions() {
|
||||||
|
await element(by.id('room-view-header-actions')).tap();
|
||||||
|
await waitFor(element(by.id('room-actions-view'))).toBeVisible().withTimeout(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openJoinCode() {
|
||||||
|
await element(by.id('room-view-join-button')).tap();
|
||||||
|
await waitFor(element(by.id('join-code'))).toBeVisible().withTimeout(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Join public room', () => {
|
||||||
|
before(async() => {
|
||||||
|
await device.launchApp({ permissions: { notifications: 'YES' }, delete: true });
|
||||||
|
await navigateToLogin();
|
||||||
|
await login(testuser.username, testuser.password);
|
||||||
|
await navigateToRoom();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Usage', async() => {
|
||||||
|
it('should tap join and ask for join code', async() => {
|
||||||
|
await openJoinCode();
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should cancel join room', async() => {
|
||||||
|
await element(by.id('join-code-cancel')).tap();
|
||||||
|
await waitFor(element(by.id('join-code'))).toBeNotVisible().withTimeout(5000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should join room', async() => {
|
||||||
|
await openJoinCode();
|
||||||
|
await element(by.id('join-code-input')).replaceText(joinCode);
|
||||||
|
await element(by.id('join-code-submit')).tap();
|
||||||
|
await waitFor(element(by.id('join-code'))).toBeNotVisible().withTimeout(5000);
|
||||||
|
await waitFor(element(by.id('messagebox'))).toBeVisible().withTimeout(60000);
|
||||||
|
await expect(element(by.id('messagebox'))).toBeVisible();
|
||||||
|
await expect(element(by.id('room-view-join'))).toBeNotVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should send message', async() => {
|
||||||
|
await mockMessage('message');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue