Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions app/definition/ITeam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// https://github.com/RocketChat/Rocket.Chat/blob/develop/definition/ITeam.ts
export const TEAM_TYPE = {
PUBLIC: 0,
PRIVATE: 1
};
11 changes: 9 additions & 2 deletions app/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -709,5 +709,12 @@
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
"Teams": "Teams",
"No_team_channels_found": "No channels found",
"Team_not_found": "Team not found"
}
"Team_not_found": "Team not found",
"Create_Team": "Create Team",
"Team_Name": "Team Name",
"Private_Team": "Private Team",
"Read_Only_Team": "Read Only Team",
"Broadcast_Team": "Broadcast Team",
"creating_team" : "creating team",
"team-name-already-exists": "A team with that name already exists"
}
20 changes: 19 additions & 1 deletion app/lib/rocketchat.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import UserPreferences from './userPreferences';
import { Encryption } from './encryption';
import EventEmitter from '../utils/events';
import { sanitizeLikeString } from './database/utils';
import { TEAM_TYPE } from '../definition/ITeam';

const TOKEN_KEY = 'reactnativemeteor_usertoken';
const CURRENT_SERVER = 'currentServer';
Expand Down Expand Up @@ -732,7 +733,24 @@ const RocketChat = {
prid, pmid, t_name, reply, users, encrypted
});
},

createTeam({
name, users, type, readOnly, broadcast, encrypted
}) {
const params = {
name,
users,
type: type ? TEAM_TYPE.PRIVATE : TEAM_TYPE.PUBLIC,
room: {
readOnly,
extraData: {
broadcast,
encrypted
}
}
};
// RC 3.13.0
return this.post('teams.create', params);
},
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0
Expand Down
36 changes: 32 additions & 4 deletions app/sagas/createChannel.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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) {
Expand All @@ -56,15 +74,25 @@ const handleRequest = function* handleRequest({ data }) {
const subCollection = db.get('subscriptions');
yield db.action(async() => {
await subCollection.create((s) => {
s._raw = sanitizedRaw({ id: sub.rid }, subCollection.schema);
s._raw = sanitizedRaw({ id: sub.team ? sub.team.roomId : sub.rid }, subCollection.schema);
Object.assign(s, sub);
});
});
} catch {
// do nothing
}

yield put(createChannelSuccess(sub));
let successParams = {};
if (data.isTeam) {
successParams = {
...sub.team,
rid: sub.team.roomId,
t: sub.team.type ? 'p' : 'c'
};
} else {
successParams = data;
}
yield put(createChannelSuccess(successParams));
} catch (err) {
logEvent(events[data.group ? 'SELECTED_USERS_CREATE_GROUP_F' : 'CR_CREATE_F']);
yield put(createChannelFailure(err));
Expand All @@ -81,7 +109,7 @@ const handleSuccess = function* handleSuccess({ data }) {

const handleFailure = function handleFailure({ err }) {
setTimeout(() => {
const msg = err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
const msg = err.data ? I18n.t(err.data.error) : err.reason || I18n.t('There_was_an_error_while_action', { action: I18n.t('creating_channel') });
showErrorAlert(msg);
}, 300);
};
Expand Down
1 change: 1 addition & 0 deletions app/utils/log/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export default {

// NEW MESSAGE VIEW
NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel',
NEW_MSG_CREATE_TEAM: 'new_msg_create_team',
NEW_MSG_CREATE_GROUP_CHAT: 'new_msg_create_group_chat',
NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion',
NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user',
Expand Down
2 changes: 2 additions & 0 deletions app/utils/room.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ export const getBadgeColor = ({ subscription, messageId, theme }) => {
};

export const makeThreadName = messageRecord => messageRecord.msg || messageRecord?.attachments[0]?.title;

export const isTeamRoom = ({ teamId, joined }) => teamId && joined;
72 changes: 46 additions & 26 deletions app/views/CreateChannelView.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,9 @@ const styles = StyleSheet.create({
});

class CreateChannelView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Create_Channel')
});

static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
baseUrl: PropTypes.string,
create: PropTypes.func.isRequired,
removeUser: PropTypes.func.isRequired,
Expand All @@ -89,12 +86,19 @@ class CreateChannelView extends React.Component {
theme: PropTypes.string
};

state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false
constructor(props) {
super(props);
const { route } = this.props;
const isTeam = route?.params?.isTeam || false;
this.state = {
channelName: '',
type: true,
readOnly: false,
encrypted: false,
broadcast: false,
isTeam
};
this.setHeader();
}

shouldComponentUpdate(nextProps, nextState) {
Expand Down Expand Up @@ -134,6 +138,15 @@ class CreateChannelView extends React.Component {
return false;
}

setHeader = () => {
const { navigation } = this.props;
const { isTeam } = this.state;

navigation.setOptions({
title: isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
});
}

toggleRightButton = (channelName) => {
const { navigation } = this.props;
navigation.setOptions({
Expand All @@ -152,9 +165,11 @@ class CreateChannelView extends React.Component {

submit = () => {
const {
channelName, type, readOnly, broadcast, encrypted
channelName, type, readOnly, broadcast, encrypted, isTeam
} = this.state;
const { users: usersProps, isFetching, create } = this.props;
const {
users: usersProps, isFetching, create
} = this.props;

if (!channelName.trim() || isFetching) {
return;
Expand All @@ -163,9 +178,9 @@ class CreateChannelView extends React.Component {
// transform users object into array of usernames
const users = usersProps.map(user => user.name);

// create channel
// create channel or team
create({
name: channelName, users, type, readOnly, broadcast, encrypted
name: channelName, users, type, readOnly, broadcast, encrypted, isTeam
});

Review.pushPositiveEvent();
Expand Down Expand Up @@ -196,11 +211,12 @@ class CreateChannelView extends React.Component {
}

renderType() {
const { type } = this.state;
const { type, isTeam } = this.state;

return this.renderSwitch({
id: 'type',
value: type,
label: 'Private_Channel',
label: isTeam ? 'Private_Team' : 'Private_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_TYPE);
// If we set the channel as public, encrypted status should be false
Expand All @@ -210,11 +226,12 @@ class CreateChannelView extends React.Component {
}

renderReadOnly() {
const { readOnly, broadcast } = this.state;
const { readOnly, broadcast, isTeam } = this.state;

return this.renderSwitch({
id: 'readonly',
value: readOnly,
label: 'Read_Only_Channel',
label: isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_READ_ONLY);
this.setState({ readOnly: value });
Expand Down Expand Up @@ -244,11 +261,12 @@ class CreateChannelView extends React.Component {
}

renderBroadcast() {
const { broadcast, readOnly } = this.state;
const { broadcast, readOnly, isTeam } = this.state;

return this.renderSwitch({
id: 'broadcast',
value: broadcast,
label: 'Broadcast_Channel',
label: isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
onValueChange: (value) => {
logEvent(events.CR_TOGGLE_BROADCAST);
this.setState({
Expand Down Expand Up @@ -301,8 +319,10 @@ class CreateChannelView extends React.Component {
}

render() {
const { channelName } = this.state;
const { users, isFetching, theme } = this.props;
const { channelName, isTeam } = this.state;
const {
users, isFetching, theme
} = this.props;
const userCount = users.length;

return (
Expand All @@ -312,18 +332,18 @@ class CreateChannelView extends React.Component {
keyboardVerticalOffset={128}
>
<StatusBar />
<SafeAreaView testID='create-channel-view'>
<SafeAreaView testID={isTeam ? 'create-team-view' : 'create-channel-view'}>
<ScrollView {...scrollPersistTaps}>
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
<TextInput
autoFocus
style={[styles.input, { backgroundColor: themes[theme].backgroundColor }]}
label={I18n.t('Channel_Name')}
label={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
value={channelName}
onChangeText={this.onChangeText}
placeholder={I18n.t('Channel_Name')}
placeholder={isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
returnKeyType='done'
testID='create-channel-name'
testID={isTeam ? 'create-team-name' : 'create-channel-name'}
autoCorrect={false}
autoCapitalize='none'
theme={theme}
Expand Down
14 changes: 13 additions & 1 deletion app/views/NewMessageView.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ class NewMessageView extends React.Component {
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
}

createTeam = () => {
logEvent(events.NEW_MSG_CREATE_TEAM);
const { navigation } = this.props;
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView', { isTeam: true }) });
}

createGroupChat = () => {
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
const { createChannel, maxUsers, navigation } = this.props;
Expand Down Expand Up @@ -172,6 +178,12 @@ class NewMessageView extends React.Component {
testID: 'new-message-view-create-channel',
first: true
})}
{this.renderButton({
onPress: this.createTeam,
title: I18n.t('Create_Team'),
icon: 'teams',
testID: 'new-message-view-create-team'
})}
{maxUsers > 2 ? this.renderButton({
onPress: this.createGroupChat,
title: I18n.t('Create_Direct_Messages'),
Expand Down Expand Up @@ -253,7 +265,7 @@ const mapStateToProps = state => ({
});

const mapDispatchToProps = dispatch => ({
createChannel: params => dispatch(createChannelRequest(params))
create: params => dispatch(createChannelRequest(params))
});

export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
10 changes: 6 additions & 4 deletions app/views/RoomView/RightButtons.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as HeaderButton from '../../containers/HeaderButton';
import database from '../../lib/database';
import { getUserSelector } from '../../selectors/login';
import { logEvent, events } from '../../utils/log';
import { isTeamRoom } from '../../utils/room';

class RightButtonsContainer extends Component {
static propTypes = {
Expand All @@ -15,10 +16,11 @@ class RightButtonsContainer extends Component {
rid: PropTypes.string,
t: PropTypes.string,
tmid: PropTypes.string,
teamId: PropTypes.bool,
teamId: PropTypes.string,
navigation: PropTypes.object,
isMasterDetail: PropTypes.bool,
toggleFollowThread: PropTypes.func
toggleFollowThread: PropTypes.func,
joined: PropTypes.bool
};

constructor(props) {
Expand Down Expand Up @@ -163,7 +165,7 @@ class RightButtonsContainer extends Component {
isFollowingThread, tunread, tunreadUser, tunreadGroup
} = this.state;
const {
t, tmid, threadsEnabled, teamId
t, tmid, threadsEnabled, teamId, joined
} = this.props;
if (t === 'l') {
return null;
Expand All @@ -181,7 +183,7 @@ class RightButtonsContainer extends Component {
}
return (
<HeaderButton.Container>
{teamId ? (
{isTeamRoom({ teamId, joined }) ? (
<HeaderButton.Item
iconName='channel-public'
onPress={this.goTeamChannels}
Expand Down
Loading