[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_users: 'Invite users',
|
||||
Join: 'Join',
|
||||
Join_Code: 'Join Code',
|
||||
Insert_Join_Code: 'Insert Join Code',
|
||||
Join_our_open_workspace: 'Join our open workspace',
|
||||
Join_your_workspace: 'Join your workspace',
|
||||
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_users: 'Convidar usuários',
|
||||
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_your_workspace: 'Entre na sua workspace',
|
||||
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
|
||||
// RC 0.48.0
|
||||
if (type === 'p') {
|
||||
return this.methodCallWrapper('joinRoom', roomId);
|
||||
}
|
||||
return this.post('channels.join', { roomId });
|
||||
return this.post('channels.join', { roomId, joinCode });
|
||||
},
|
||||
triggerBlockAction,
|
||||
triggerSubmitView,
|
||||
|
|
|
@ -149,8 +149,9 @@ class DirectoryView extends React.Component {
|
|||
this.goRoom({ rid: result.room._id, name: item.username, t: 'd' });
|
||||
}
|
||||
} else {
|
||||
const { room } = await RocketChat.getRoomInfo(item._id);
|
||||
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 ReactionPicker from './ReactionPicker';
|
||||
import UploadProgress from './UploadProgress';
|
||||
import JoinCode from './JoinCode';
|
||||
import styles from './styles';
|
||||
import log, { logEvent, events } from '../../utils/log';
|
||||
import EventEmitter from '../../utils/events';
|
||||
|
@ -73,7 +74,7 @@ const stateAttrsUpdate = [
|
|||
'readOnly',
|
||||
'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 {
|
||||
static propTypes = {
|
||||
|
@ -152,6 +153,7 @@ class RoomView extends React.Component {
|
|||
|
||||
this.messagebox = React.createRef();
|
||||
this.list = React.createRef();
|
||||
this.joinCode = React.createRef();
|
||||
this.mounted = false;
|
||||
if (this.rid) {
|
||||
this.sub = new RoomClass(this.rid);
|
||||
|
@ -711,6 +713,12 @@ class RoomView extends React.Component {
|
|||
|
||||
setLastOpen = lastOpen => this.setState({ lastOpen });
|
||||
|
||||
onJoin = () => {
|
||||
this.internalSetState({
|
||||
joined: true
|
||||
});
|
||||
}
|
||||
|
||||
joinRoom = async() => {
|
||||
logEvent(events.ROOM_JOIN);
|
||||
try {
|
||||
|
@ -719,11 +727,14 @@ class RoomView extends React.Component {
|
|||
if (this.isOmnichannel) {
|
||||
await takeInquiry(room._id);
|
||||
} 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) {
|
||||
log(e);
|
||||
}
|
||||
|
@ -1086,6 +1097,13 @@ class RoomView extends React.Component {
|
|||
onClose={this.onCloseReactionsModal}
|
||||
getCustomEmoji={this.getCustomEmoji}
|
||||
/>
|
||||
<JoinCode
|
||||
ref={this.joinCode}
|
||||
onJoin={this.onJoin}
|
||||
rid={rid}
|
||||
t={t}
|
||||
theme={theme}
|
||||
/>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
|||
channels: {
|
||||
detoxpublic: {
|
||||
name: 'detox-public'
|
||||
},
|
||||
detoxpublicprotected: {
|
||||
name: 'detox-public-protected',
|
||||
joinCode: '123'
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
|||
channels: {
|
||||
detoxpublic: {
|
||||
name: 'detox-public'
|
||||
},
|
||||
detoxpublicprotected: {
|
||||
name: 'detox-public-protected',
|
||||
joinCode: '123'
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
|
|
|
@ -31,6 +31,10 @@ const data = {
|
|||
channels: {
|
||||
detoxpublic: {
|
||||
name: 'detox-public'
|
||||
},
|
||||
detoxpublicprotected: {
|
||||
name: 'detox-public-protected',
|
||||
joinCode: '123'
|
||||
}
|
||||
},
|
||||
groups: {
|
||||
|
|
|
@ -40,12 +40,14 @@ const createUser = async (username, password, name, email) => {
|
|||
const createChannelIfNotExists = async (channelname) => {
|
||||
console.log(`Creating public channel ${channelname}`)
|
||||
try {
|
||||
await rocketchat.post('channels.create', {
|
||||
const room = await rocketchat.post('channels.create', {
|
||||
"name": channelname
|
||||
})
|
||||
return room
|
||||
} catch (createError) {
|
||||
try { //Maybe it exists already?
|
||||
await rocketchat.get(`channels.info?roomName=${channelname}`)
|
||||
const room = rocketchat.get(`channels.info?roomName=${channelname}`)
|
||||
return room
|
||||
} catch (infoError) {
|
||||
console.log(JSON.stringify(createError))
|
||||
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) => {
|
||||
console.log(`Sending message to ${groupname}`)
|
||||
try {
|
||||
|
@ -96,7 +116,11 @@ const setup = async () => {
|
|||
for (var channelKey in data.channels) {
|
||||
if (data.channels.hasOwnProperty(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