diff --git a/app/actions/createTeam.js b/app/actions/createTeam.js deleted file mode 100644 index c91cce2d7..000000000 --- a/app/actions/createTeam.js +++ /dev/null @@ -1,22 +0,0 @@ -import * as types from './actionsTypes'; - -export function createTeamRequest(data) { - return { - type: types.CREATE_TEAM.REQUEST, - data - }; -} - -export function createTeamSuccess(data) { - return { - type: types.CREATE_TEAM.SUCCESS, - data - }; -} - -export function createTeamFailure(err) { - return { - type: types.CREATE_TEAM.FAILURE, - err - }; -} diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js index 9dfa39857..965f1c211 100644 --- a/app/lib/rocketchat.js +++ b/app/lib/rocketchat.js @@ -731,10 +731,20 @@ const RocketChat = { createTeam({ name, users, type, readOnly, broadcast, encrypted }) { + const params = { + name, + users, + type: type ? 1 : 0, + room: { + readOnly, + extraData: { + broadcast, + encrypted + } + } + }; // RC 3.13.0 - return this.post('teams.create', { - name, users, type, readOnly, broadcast, encrypted - }); + return this.post('teams.create', params); }, joinRoom(roomId, joinCode, type) { // TODO: join code diff --git a/app/reducers/createTeam.js b/app/reducers/createTeam.js deleted file mode 100644 index e962c6315..000000000 --- a/app/reducers/createTeam.js +++ /dev/null @@ -1,36 +0,0 @@ -import { CREATE_TEAM } from '../actions/actionsTypes'; - -const initialState = { - isFetching: false, - failure: false, - result: {}, - error: {} -}; - -export default function(state = initialState, action) { - switch (action.type) { - case CREATE_TEAM.REQUEST: - return { - ...state, - isFetching: true, - failure: false, - error: {} - }; - case CREATE_TEAM.SUCCESS: - return { - ...state, - isFetching: false, - failure: false, - result: action.data - }; - case CREATE_TEAM.FAILURE: - return { - ...state, - isFetching: false, - failure: true, - error: action.err - }; - default: - return state; - } -} diff --git a/app/reducers/index.js b/app/reducers/index.js index aaad34fce..dfee5f3eb 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -7,7 +7,6 @@ import rooms from './rooms'; import server from './server'; import selectedUsers from './selectedUsers'; import createChannel from './createChannel'; -import createTeam from './createTeam'; import app from './app'; import sortPreferences from './sortPreferences'; import share from './share'; @@ -30,7 +29,6 @@ export default combineReducers({ server, selectedUsers, createChannel, - createTeam, app, room, rooms, diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js index 613aedec2..eb665a094 100644 --- a/app/sagas/createChannel.js +++ b/app/sagas/createChannel.js @@ -21,6 +21,10 @@ const createGroupChat = function createGroupChat() { return RocketChat.createGroupChat(); }; +const createTeam = function createTeam(data) { + return RocketChat.createTeam(data); +}; + const handleRequest = function* handleRequest({ data }) { try { const auth = yield select(state => state.login.isAuthenticated); @@ -29,7 +33,21 @@ const handleRequest = function* handleRequest({ data }) { } let sub; - if (data.group) { + if (data.isTeam) { + const { + type, + readOnly, + broadcast, + encrypted + } = data; + logEvent(events.CR_CREATE, { + type, + readOnly, + broadcast, + encrypted + }); + sub = yield call(createTeam, data); + } else if (data.group) { logEvent(events.SELECTED_USERS_CREATE_GROUP); const result = yield call(createGroupChat); if (result.success) { @@ -76,7 +94,11 @@ const handleSuccess = function* handleSuccess({ data }) { if (isMasterDetail) { Navigation.navigate('DrawerNavigator'); } - goRoom({ item: data, isMasterDetail }); + if (data.team) { + goRoom({ item: data.team, isMasterDetail }); + } else { + goRoom({ item: data, isMasterDetail }); + } }; const handleFailure = function handleFailure({ err }) { diff --git a/app/sagas/createTeam.js b/app/sagas/createTeam.js deleted file mode 100644 index 7da3e0394..000000000 --- a/app/sagas/createTeam.js +++ /dev/null @@ -1,80 +0,0 @@ -import { - select, put, call, take, takeLatest -} from 'redux-saga/effects'; -import { sanitizedRaw } from '@nozbe/watermelondb/RawRecord'; - -import { CREATE_TEAM, LOGIN } from '../actions/actionsTypes'; -import { createTeamSuccess, createTeamFailure } from '../actions/createTeam'; -import { showErrorAlert } from '../utils/info'; -import RocketChat from '../lib/rocketchat'; -import Navigation from '../lib/Navigation'; -import database from '../lib/database'; -import I18n from '../i18n'; -import { logEvent, events } from '../utils/log'; -import { goRoom } from '../utils/goRoom'; - -const createTeam = function createTeam(data) { - return RocketChat.createTeam(data); -}; - -const handleRequest = function* handleRequest({ data }) { - try { - const auth = yield select(state => state.login.isAuthenticated); - if (!auth) { - yield take(LOGIN.SUCCESS); - } - - const { - type, readOnly, broadcast, encrypted - } = data; - - logEvent(events.CR_CREATE, { - type, - readOnly, - broadcast, - encrypted - }); - const sub = yield call(createTeam, data); - - try { - const db = database.active; - const subCollection = db.get('subscriptions'); - yield db.action(async() => { - await subCollection.create((s) => { - s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema); - Object.assign(s, sub); - }); - }); - } catch { - // do nothing - } - - yield put(createTeamSuccess(sub)); - } catch (err) { - logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']); - yield put(createTeamFailure(err)); - } -}; - -const handleSuccess = function* handleSuccess({ data }) { - const isMasterDetail = yield select(state => state.app.isMasterDetail); - if (isMasterDetail) { - Navigation.navigate('DrawerNavigator'); - } - goRoom({ item: data, isMasterDetail }); -}; - -const handleFailure = function handleFailure({ err }) { - setTimeout(() => { - const msg = err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_team') }); - showErrorAlert(msg); - }, 300); -}; - -const root = function* root() { - yield takeLatest(CREATE_TEAM.REQUEST, handleRequest); - yield takeLatest(CREATE_TEAM.SUCCESS, handleSuccess); - yield takeLatest(CREATE_TEAM.FAILURE, handleFailure); -}; - -export default root; diff --git a/app/sagas/index.js b/app/sagas/index.js index b5787d424..e499d74ec 100644 --- a/app/sagas/index.js +++ b/app/sagas/index.js @@ -5,7 +5,6 @@ import room from './room'; import messages from './messages'; import selectServer from './selectServer'; import createChannel from './createChannel'; -import createTeam from './createTeam'; import init from './init'; import state from './state'; import deepLinking from './deepLinking'; @@ -19,7 +18,6 @@ const root = function* root() { yield all([ init(), createChannel(), - createTeam(), rooms(), room(), login(), diff --git a/app/utils/goRoom.js b/app/utils/goRoom.js index 94adfde49..40fba6e97 100644 --- a/app/utils/goRoom.js +++ b/app/utils/goRoom.js @@ -7,18 +7,27 @@ const navigate = ({ item, isMasterDetail, ...props }) => { if (isMasterDetail) { navigationMethod = Navigation.replace; } - - navigationMethod('RoomView', { - rid: item.rid, - name: RocketChat.getRoomTitle(item), - t: item.t, - prid: item.prid, - room: item, - search: item.search, - visitor: item.visitor, - roomUserId: RocketChat.getUidDirectMessage(item), - ...props - }); + console.log({ item }); + if (item.roomId) { + navigationMethod('RoomView', { + rid: item.roomId || item.rid, + name: RocketChat.getRoomTitle(item), + roomUserId: RocketChat.getUidDirectMessage(item), + ...props + }); + } else { + navigationMethod('RoomView', { + rid: item.rid, + name: RocketChat.getRoomTitle(item), + t: item.t, + prid: item.prid, + room: item, + search: item.search, + visitor: item.visitor, + roomUserId: RocketChat.getUidDirectMessage(item), + ...props + }); + } }; export const goRoom = async({ item = {}, isMasterDetail = false, ...props }) => { diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js index 9a300b8af..59b2ac06e 100644 --- a/app/views/CreateChannelView.js +++ b/app/views/CreateChannelView.js @@ -10,7 +10,6 @@ import * as List from '../containers/List'; import TextInput from '../presentation/TextInput'; import Loading from '../containers/Loading'; import { createChannelRequest as createChannelRequestAction } from '../actions/createChannel'; -import { createTeamRequest as createTeamRequestAction } from '../actions/createTeam'; import { removeUser as removeUserAction } from '../actions/selectedUsers'; import sharedStyles from './Styles'; import KeyboardView from '../presentation/KeyboardView'; @@ -77,8 +76,7 @@ class CreateChannelView extends React.Component { navigation: PropTypes.object, route: PropTypes.object, baseUrl: PropTypes.string, - createChannel: PropTypes.func.isRequired, - createTeam: PropTypes.func.isRequired, + create: PropTypes.func.isRequired, removeUser: PropTypes.func.isRequired, error: PropTypes.object, failure: PropTypes.bool, @@ -158,7 +156,7 @@ class CreateChannelView extends React.Component { channelName, type, readOnly, broadcast, encrypted } = this.state; const { - users: usersProps, isFetching, createTeam, createChannel, route + users: usersProps, isFetching, create, route } = this.props; const { isTeam } = route?.params; @@ -169,17 +167,10 @@ class CreateChannelView extends React.Component { // transform users object into array of usernames const users = usersProps.map(user => user.name); - if (isTeam) { - // create team - createTeam({ - name: channelName, users, type, readOnly, broadcast, encrypted - }); - } else { - // create channel - createChannel({ - name: channelName, users, type, readOnly, broadcast, encrypted - }); - } + // create channel or team + create({ + name: channelName, users, type, readOnly, broadcast, encrypted, isTeam + }); Review.pushPositiveEvent(); } @@ -376,20 +367,16 @@ class CreateChannelView extends React.Component { } } -const mapStateToProps = (state, ownProps) => { - const { route } = ownProps; - return { - baseUrl: state.server.server, - isFetching: route?.params?.isTeam ? state.createTeam.isFetching : state.createChannel.isFetching, - encryptionEnabled: state.encryption.enabled, - users: state.selectedUsers.users, - user: getUserSelector(state) - }; -}; +const mapStateToProps = state => ({ + baseUrl: state.server.server, + isFetching: state.createChannel.isFetching, + encryptionEnabled: state.encryption.enabled, + users: state.selectedUsers.users, + user: getUserSelector(state) +}); const mapDispatchToProps = dispatch => ({ - createChannel: data => dispatch(createChannelRequestAction(data)), - createTeam: data => dispatch(createTeamRequestAction(data)), + create: data => dispatch(createChannelRequestAction(data)), removeUser: user => dispatch(removeUserAction(user)) }); diff --git a/app/views/NewMessageView.js b/app/views/NewMessageView.js index d8cdc01cb..8d916e456 100644 --- a/app/views/NewMessageView.js +++ b/app/views/NewMessageView.js @@ -23,7 +23,6 @@ import { withTheme } from '../theme'; import { getUserSelector } from '../selectors/login'; import Navigation from '../lib/Navigation'; import { createChannelRequest } from '../actions/createChannel'; -import { createTeamRequest } from '../actions/createTeam'; import { goRoom } from '../utils/goRoom'; import SafeAreaView from '../containers/SafeAreaView'; @@ -266,8 +265,7 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - createChannel: params => dispatch(createChannelRequest(params)), - createTeam: params => dispatch(createTeamRequest(params)) + create: params => dispatch(createChannelRequest(params)) }); export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView)); diff --git a/e2e/data.js b/e2e/data.js index 79e7842e8..c69b72515 100644 --- a/e2e/data.js +++ b/e2e/data.js @@ -42,6 +42,11 @@ const data = { name: `detox-private-${ value }` } }, + teams: { + private: { + name: `detox-team-${ value }` + } + }, registeringUser: { username: `newuser${ value }`, password: `password${ value }`, diff --git a/e2e/helpers/data_setup.js b/e2e/helpers/data_setup.js index 1f8f8fb65..9d0dad492 100644 --- a/e2e/helpers/data_setup.js +++ b/e2e/helpers/data_setup.js @@ -57,6 +57,26 @@ const createChannelIfNotExists = async (channelname) => { } } +const createTeamIfNotExists = async (teamname) => { + console.log(`Creating private team ${teamname}`) + try { + const team = await rocketchat.post('teams.create', { + "name": teamname, + "type": 1 + }) + return team + } catch (createError) { + try { //Maybe it exists already? + const team = rocketchat.get(`teams.info?teamName=${teamname}`) + return team + } catch (infoError) { + console.log(JSON.stringify(createError)) + console.log(JSON.stringify(infoError)) + throw "Failed to find or create private team" + } + } +} + const createGroupIfNotExists = async (groupname) => { console.log(`Creating private group ${groupname}`) try { @@ -133,6 +153,13 @@ const setup = async () => { } } + for (var teamKey in data.teams) { + if (data.teams.hasOwnProperty(teamKey)) { + const team = data.teams[teamKey] + await createTeamIfNotExists(team.name) + } + } + return } diff --git a/e2e/tests/team/01-createteam.spec.js b/e2e/tests/team/01-createteam.spec.js new file mode 100644 index 000000000..6e06b69f0 --- /dev/null +++ b/e2e/tests/team/01-createteam.spec.js @@ -0,0 +1,82 @@ +const { + device, expect, element, by, waitFor +} = require('detox'); +const data = require('../../data'); +const { tapBack, sleep, navigateToLogin, login, tryTapping } = require('../../helpers/app'); + + + +describe('Create team screen', () => { + before(async() => { + await device.launchApp({ permissions: { notifications: 'YES' }, delete: true }); + await navigateToLogin(); + await login(data.users.regular.username, data.users.regular.password); + }); + + describe('New Message', async() => { + before(async() => { + await element(by.id('rooms-list-view-create-channel')).tap(); + }); + + describe('Render', async() => { + it('should have team button', async() => { + await waitFor(element(by.id('new-message-view-create-team'))).toBeVisible().withTimeout(2000); + }); + }) + + describe('Usage', async() => { + it('should navigate to select users', async() => { + await element(by.id('new-message-view-create-team')).tap(); + await waitFor(element(by.id('select-users-view'))).toExist().withTimeout(5000); + }); + }) + }); + + describe('Select Users', async() => { + it('should search users', async() => { + await element(by.id('select-users-view-search')).replaceText('rocket.cat'); + await waitFor(element(by.id(`select-users-view-item-rocket.cat`))).toBeVisible().withTimeout(10000); + }); + + it('should select/unselect user', async() => { + // Spotlight issues + await element(by.id('select-users-view-item-rocket.cat')).tap(); + await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000); + await element(by.id('selected-user-rocket.cat')).tap(); + await waitFor(element(by.id('selected-user-rocket.cat'))).toBeNotVisible().withTimeout(10000); + // Spotlight issues + await element(by.id('select-users-view-item-rocket.cat')).tap(); + await waitFor(element(by.id('selected-user-rocket.cat'))).toBeVisible().withTimeout(10000); + }); + + it('should create team', async() => { + await element(by.id('selected-users-view-submit')).tap(); + await waitFor(element(by.id('create-team-view'))).toExist().withTimeout(10000); + }); + }) + + describe('Create Team', async() => { + describe('Usage', async() => { + it('should get invalid team name', async() => { + await element(by.id('create-team-name')).typeText(`${data.teams.private.name}`); + await element(by.id('create-channel-submit')).tap(); + await element(by.text('OK')).tap(); + }); + + it('should create private team', async() => { + const room = `private${ data.random }`; + await element(by.id('create-team-name')).replaceText(''); + await element(by.id('create-team-name')).typeText(room); + await element(by.id('create-channel-submit')).tap(); + await waitFor(element(by.id('room-view'))).toExist().withTimeout(20000); + await expect(element(by.id('room-view'))).toExist(); + await waitFor(element(by.id(`room-view-title-${ room }`))).toExist().withTimeout(6000); + await expect(element(by.id(`room-view-title-${ room }`))).toExist(); + await tapBack(); + await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000); + await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(6000); + await expect(element(by.id(`rooms-list-view-item-${ room }`))).toExist(); + }); + }) + }); +});