Compare commits
22 Commits
develop
...
new.conver
Author | SHA1 | Date |
---|---|---|
Gerzon Z | a305676fd6 | |
Gerzon Z | 9cf3e68721 | |
Gerzon Z | 04ea01b324 | |
Gerzon Z | b961fa05d9 | |
Gerzon Z | 4b6e691d8a | |
Gerzon Z | 4bac054105 | |
Gerzon Z | 7d2924e2e9 | |
Gerzon Z | 4761e21de8 | |
Gerzon Z | 69ae247329 | |
Gerzon Z | c3ffa37323 | |
Gerzon Z | bb0632b689 | |
Gerzon Z | 37421d395a | |
Gerzon Z | 0cf173d7ba | |
Gerzon Z | 69214bee93 | |
Gerzon Z | 1ff77118f5 | |
Gerzon Z | 16fd57527a | |
Gerzon Z | a245af6c1c | |
Gerzon Z | c8b8680541 | |
Gerzon Z | 8b82bd456e | |
Gerzon Z | 585a9aea9a | |
Gerzon Z | c8a88c9b02 | |
Gerzon Z | 2f5f247915 |
File diff suppressed because it is too large
Load Diff
|
@ -83,8 +83,7 @@ const Button = React.memo(({
|
|||
}) => (
|
||||
<Touch
|
||||
onPress={() => onPress(props.title)}
|
||||
style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
|
||||
underlayColor={underlayColor}
|
||||
style={({ pressed }) => [{ backgroundColor: pressed ? underlayColor || themes[props.theme].bannerBackground : backgroundColor || themes[props.theme].backgroundColor }]}
|
||||
enabled={!props.disabled}
|
||||
theme={props.theme}
|
||||
>
|
||||
|
|
|
@ -7,6 +7,7 @@ import { connect } from 'react-redux';
|
|||
import { Base64 } from 'js-base64';
|
||||
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||
|
||||
import { transparentize } from 'color2k';
|
||||
import { withTheme } from '../theme';
|
||||
import sharedStyles from '../views/Styles';
|
||||
import { themes } from '../constants/colors';
|
||||
|
@ -344,10 +345,8 @@ class LoginServices extends React.PureComponent {
|
|||
<Touch
|
||||
key={service.name}
|
||||
onPress={onPress}
|
||||
style={[styles.serviceButton, { backgroundColor }]}
|
||||
style={({ pressed }) => [styles.serviceButton, { backgroundColor: pressed ? transparentize(themes[theme].buttonText, 0.5) : backgroundColor }]}
|
||||
theme={theme}
|
||||
activeOpacity={0.5}
|
||||
underlayColor={themes[theme].buttonText}
|
||||
>
|
||||
<View style={styles.serviceButtonContainer}>
|
||||
{service.authType === 'oauth' || service.authType === 'apple' ? <CustomIcon name={icon} size={24} color={themes[theme].titleText} style={styles.serviceIcon} /> : null}
|
||||
|
|
|
@ -14,7 +14,7 @@ const Button = React.memo(({
|
|||
|
||||
return (
|
||||
<Touch
|
||||
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
||||
style={({ pressed }) => [styles.buttonView, { backgroundColor: pressed ? 'transparent' : themes[theme].bannerBackground }]}
|
||||
underlayColor={themes[theme].passcodeButtonActive}
|
||||
rippleColor={themes[theme].passcodeButtonActive}
|
||||
enabled={!disabled}
|
||||
|
|
|
@ -290,6 +290,7 @@
|
|||
"last_message": "last message",
|
||||
"Leave_channel": "Leave channel",
|
||||
"leaving_room": "leaving room",
|
||||
"Leave": "Leave",
|
||||
"leave": "leave",
|
||||
"Legal": "Legal",
|
||||
"Light": "Light",
|
||||
|
@ -435,6 +436,7 @@
|
|||
"Review_app_unable_store": "Unable to open {{store}}",
|
||||
"Review_this_app": "Review this app",
|
||||
"Remove": "Remove",
|
||||
"remove": "remove",
|
||||
"Roles": "Roles",
|
||||
"Room_actions": "Room actions",
|
||||
"Room_changed_announcement": "Room announcement changed to: {{announcement}} by {{userBy}}",
|
||||
|
@ -709,5 +711,33 @@
|
|||
"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",
|
||||
"Add_Channel_to_Team": "Add Channel to Team",
|
||||
"Create_New": "Create New",
|
||||
"Add_Existing": "Add Existing",
|
||||
"Add_Existing_Channel": "Add Existing Channel",
|
||||
"Remove_from_Team": "Remove from Team",
|
||||
"Auto-join": "Auto-join",
|
||||
"Delete_Team_Room_Warning": "Woud you like to remove this channel from the team? The channel will be moved back to the workspace",
|
||||
"Confirmation": "Confirmation",
|
||||
"invalid-room": "Invalid room",
|
||||
"You_are_leaving_the_team": "You are leaving the team '{{team}}'",
|
||||
"Leave_Team": "Leave Team",
|
||||
"Select_Teams": "Select the Team's channels you would like to leave.",
|
||||
"Cannot_leave": "Cannot leave",
|
||||
"Last_owner_team_room": "You are the last owner of this channel. Once you leave the team, the channel will be kept inside the team but you will be managing it from outside.",
|
||||
"last-owner-can-not-be-removed": "Last owner cannot be removed",
|
||||
"Removing_user_from_this_Team": "You are removing {{user}} from this Team",
|
||||
"Remove_User_Teams": "Select channels you want the user to be removed from.",
|
||||
"Remove_Member": "Remove Member",
|
||||
"Error": "Error",
|
||||
"Delete_Team": "Delete Team",
|
||||
"Delete_Team_Warning": "You are deleting this team."
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ const PERMISSIONS = [
|
|||
'add-user-to-any-c-room',
|
||||
'add-user-to-any-p-room',
|
||||
'add-user-to-joined-room',
|
||||
'add-team-member',
|
||||
'add-team-channel',
|
||||
'archive-room',
|
||||
'auto-translate',
|
||||
'create-invite-links',
|
||||
|
@ -21,11 +23,14 @@ const PERMISSIONS = [
|
|||
'delete-p',
|
||||
'edit-message',
|
||||
'edit-room',
|
||||
'edit-team-member',
|
||||
'edit-team-channel',
|
||||
'force-delete-message',
|
||||
'mute-user',
|
||||
'pin-message',
|
||||
'post-readonly',
|
||||
'remove-user',
|
||||
'remove-team-channel',
|
||||
'set-leader',
|
||||
'set-moderator',
|
||||
'set-owner',
|
||||
|
@ -38,7 +43,9 @@ const PERMISSIONS = [
|
|||
'view-privileged-setting',
|
||||
'view-room-administration',
|
||||
'view-statistics',
|
||||
'view-user-administration'
|
||||
'view-user-administration',
|
||||
'view-all-teams',
|
||||
'view-all-team-channels'
|
||||
];
|
||||
|
||||
export async function setPermissions() {
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
import log from '../../utils/log';
|
||||
import updateMessages from './updateMessages';
|
||||
|
||||
async function load({ rid: roomId, latest, t }) {
|
||||
let params = { roomId, count: 50 };
|
||||
async function load({
|
||||
rid: roomId, latest, t, team
|
||||
}) {
|
||||
let params = { roomId: roomId || team.roomId, count: 50 };
|
||||
let apiType;
|
||||
if (latest) {
|
||||
params = { ...params, latest: new Date(latest).toISOString() };
|
||||
}
|
||||
|
||||
const apiType = this.roomTypeToApiType(t);
|
||||
if (team.type) {
|
||||
apiType = this.roomTypeToApiType('p');
|
||||
} else {
|
||||
apiType = this.roomTypeToApiType(t || 'c');
|
||||
}
|
||||
if (!apiType) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
@ -728,7 +728,54 @@ const RocketChat = {
|
|||
prid, pmid, t_name, reply, users, encrypted
|
||||
});
|
||||
},
|
||||
|
||||
createTeam({
|
||||
name, users, type, readOnly, broadcast, encrypted
|
||||
}) {
|
||||
const params = {
|
||||
name,
|
||||
users,
|
||||
type,
|
||||
room: {
|
||||
readOnly,
|
||||
extraData: {
|
||||
broadcast,
|
||||
encrypted
|
||||
}
|
||||
}
|
||||
};
|
||||
// RC 3.13.0
|
||||
return this.post('teams.create', params);
|
||||
},
|
||||
addTeamRooms({ rooms, teamId }) {
|
||||
const params = {
|
||||
rooms: Array.isArray(rooms) ? rooms : [rooms],
|
||||
teamId
|
||||
};
|
||||
// RC 3.13.0
|
||||
return this.post('teams.addRooms', params);
|
||||
},
|
||||
removeTeamRoom({ roomId, teamId }) {
|
||||
// RC 3.13.0
|
||||
return this.post('teams.removeRoom', { roomId, teamId });
|
||||
},
|
||||
leaveTeam({ teamName }) {
|
||||
// RC 3.13.0
|
||||
return this.post('teams.leave', { teamName });
|
||||
},
|
||||
addTeamMember(teamName) {
|
||||
const { users } = reduxStore.getState().selectedUsers;
|
||||
const members = users.map(u => ({ userId: u._id, roles: ['member'] }));
|
||||
// RC 3.13.0
|
||||
return this.post('teams.addMembers', { teamName, members });
|
||||
},
|
||||
removeTeamMember({ teamName, userId }) {
|
||||
// RC 3.13.0
|
||||
return this.post('teams.removeMember', { teamName, userId });
|
||||
},
|
||||
deleteTeam({ teamName }) {
|
||||
// RC 3.13.0
|
||||
return this.post('teams.delete', { teamName });
|
||||
},
|
||||
joinRoom(roomId, joinCode, type) {
|
||||
// TODO: join code
|
||||
// RC 0.48.0
|
||||
|
|
|
@ -24,7 +24,6 @@ const RoomItem = ({
|
|||
status,
|
||||
useRealName,
|
||||
theme,
|
||||
isFocused,
|
||||
isGroupChat,
|
||||
isRead,
|
||||
date,
|
||||
|
@ -42,6 +41,7 @@ const RoomItem = ({
|
|||
testID,
|
||||
swipeEnabled,
|
||||
onPress,
|
||||
onLongPress,
|
||||
toggleFav,
|
||||
toggleRead,
|
||||
hideChannel,
|
||||
|
@ -49,6 +49,7 @@ const RoomItem = ({
|
|||
}) => (
|
||||
<Touchable
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
width={width}
|
||||
favorite={favorite}
|
||||
toggleFav={toggleFav}
|
||||
|
@ -59,7 +60,6 @@ const RoomItem = ({
|
|||
testID={testID}
|
||||
type={type}
|
||||
theme={theme}
|
||||
isFocused={isFocused}
|
||||
swipeEnabled={swipeEnabled}
|
||||
>
|
||||
<Wrapper
|
||||
|
@ -161,7 +161,6 @@ RoomItem.propTypes = {
|
|||
status: PropTypes.string,
|
||||
useRealName: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
isFocused: PropTypes.bool,
|
||||
isGroupChat: PropTypes.bool,
|
||||
isRead: PropTypes.bool,
|
||||
teamMain: PropTypes.bool,
|
||||
|
@ -181,6 +180,7 @@ RoomItem.propTypes = {
|
|||
toggleFav: PropTypes.func,
|
||||
toggleRead: PropTypes.func,
|
||||
onPress: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
hideChannel: PropTypes.func
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ class Touchable extends React.Component {
|
|||
static propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
onPress: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
testID: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
favorite: PropTypes.bool,
|
||||
|
@ -27,7 +28,6 @@ class Touchable extends React.Component {
|
|||
hideChannel: PropTypes.func,
|
||||
children: PropTypes.element,
|
||||
theme: PropTypes.string,
|
||||
isFocused: PropTypes.bool,
|
||||
swipeEnabled: PropTypes.bool
|
||||
}
|
||||
|
||||
|
@ -203,9 +203,21 @@ class Touchable extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
onLongPress = () => {
|
||||
const { rowState } = this.state;
|
||||
if (rowState !== 0) {
|
||||
this.close();
|
||||
return;
|
||||
}
|
||||
const { onLongPress } = this.props;
|
||||
if (onLongPress) {
|
||||
onLongPress();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled
|
||||
testID, isRead, width, favorite, children, theme, swipeEnabled
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -239,11 +251,10 @@ class Touchable extends React.Component {
|
|||
>
|
||||
<Touch
|
||||
onPress={this.onPress}
|
||||
onLongPress={this.onLongPress}
|
||||
theme={theme}
|
||||
testID={testID}
|
||||
style={{
|
||||
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
|
||||
}}
|
||||
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].chatComponentBackground : themes[theme].backgroundColor }]}
|
||||
>
|
||||
{children}
|
||||
</Touch>
|
||||
|
|
|
@ -14,7 +14,6 @@ const attrs = [
|
|||
'status',
|
||||
'connected',
|
||||
'theme',
|
||||
'isFocused',
|
||||
'forceUpdate',
|
||||
'showLastMessage'
|
||||
];
|
||||
|
@ -25,6 +24,7 @@ class RoomItemContainer extends React.Component {
|
|||
showLastMessage: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
onPress: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
username: PropTypes.string,
|
||||
avatarSize: PropTypes.number,
|
||||
width: PropTypes.number,
|
||||
|
@ -36,7 +36,6 @@ class RoomItemContainer extends React.Component {
|
|||
getUserPresence: PropTypes.func,
|
||||
connected: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
isFocused: PropTypes.bool,
|
||||
getRoomTitle: PropTypes.func,
|
||||
getRoomAvatar: PropTypes.func,
|
||||
getIsGroupChat: PropTypes.func,
|
||||
|
@ -112,6 +111,11 @@ class RoomItemContainer extends React.Component {
|
|||
return onPress(item);
|
||||
}
|
||||
|
||||
onLongPress = () => {
|
||||
const { item, onLongPress } = this.props;
|
||||
return onLongPress(item);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
item,
|
||||
|
@ -123,7 +127,6 @@ class RoomItemContainer extends React.Component {
|
|||
toggleRead,
|
||||
hideChannel,
|
||||
theme,
|
||||
isFocused,
|
||||
avatarSize,
|
||||
status,
|
||||
showLastMessage,
|
||||
|
@ -160,6 +163,7 @@ class RoomItemContainer extends React.Component {
|
|||
isGroupChat={this.isGroupChat}
|
||||
isRead={isRead}
|
||||
onPress={this.onPress}
|
||||
onLongPress={this.onLongPress}
|
||||
date={date}
|
||||
accessibilityLabel={accessibilityLabel}
|
||||
width={width}
|
||||
|
@ -171,7 +175,6 @@ class RoomItemContainer extends React.Component {
|
|||
testID={testID}
|
||||
type={item.t}
|
||||
theme={theme}
|
||||
isFocused={isFocused}
|
||||
size={avatarSize}
|
||||
prid={item.prid}
|
||||
status={status}
|
||||
|
|
|
@ -21,6 +21,14 @@ const createGroupChat = function createGroupChat() {
|
|||
return RocketChat.createGroupChat();
|
||||
};
|
||||
|
||||
const createTeam = function createTeam(data) {
|
||||
return RocketChat.createTeam(data);
|
||||
};
|
||||
|
||||
const addTeamRoom = function addRoomToTeam(params) {
|
||||
return RocketChat.addTeamRooms(params);
|
||||
};
|
||||
|
||||
const handleRequest = function* handleRequest({ data }) {
|
||||
try {
|
||||
const auth = yield select(state => state.login.isAuthenticated);
|
||||
|
@ -29,7 +37,21 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
}
|
||||
|
||||
let sub;
|
||||
if (data.group) {
|
||||
if (data.isTeam) {
|
||||
const {
|
||||
type,
|
||||
readOnly,
|
||||
broadcast,
|
||||
encrypted
|
||||
} = data;
|
||||
logEvent(events.CT_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) {
|
||||
|
@ -49,14 +71,22 @@ const handleRequest = function* handleRequest({ data }) {
|
|||
encrypted
|
||||
});
|
||||
sub = yield call(createChannel, data);
|
||||
}
|
||||
|
||||
if (data.teamId) {
|
||||
logEvent(events.CT_ADD_ROOM_TO_TEAM);
|
||||
const channels = yield call(addTeamRoom, { rooms: sub.rid, teamId: data.teamId });
|
||||
if (channels.success) {
|
||||
sub.teamId = channels.rooms[0].teamId;
|
||||
sub.isTeamChannel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
s._raw = sanitizedRaw({ id: sub.team ? sub.team.roomId : sub.rid, team_id: sub.teamId }, subCollection.schema);
|
||||
Object.assign(s, sub);
|
||||
});
|
||||
});
|
||||
|
@ -76,12 +106,12 @@ const handleSuccess = function* handleSuccess({ data }) {
|
|||
if (isMasterDetail) {
|
||||
Navigation.navigate('DrawerNavigator');
|
||||
}
|
||||
goRoom({ item: data, isMasterDetail });
|
||||
goRoom({ item: data.team ? data.team : data, isMasterDetail });
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
|
|
@ -71,6 +71,9 @@ import ShareView from '../views/ShareView';
|
|||
import CreateDiscussionView from '../views/CreateDiscussionView';
|
||||
|
||||
import QueueListView from '../ee/omnichannel/views/QueueListView';
|
||||
import AddChannelTeamView from '../views/AddChannelTeamView';
|
||||
import AddExistingChannelView from '../views/AddExistingChannelView';
|
||||
import SelectListView from '../views/SelectListView';
|
||||
|
||||
// ChatsStackNavigator
|
||||
const ChatsStack = createStackNavigator();
|
||||
|
@ -91,6 +94,11 @@ const ChatsStackNavigator = () => {
|
|||
component={RoomActionsView}
|
||||
options={RoomActionsView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen
|
||||
name='SelectListView'
|
||||
component={SelectListView}
|
||||
options={SelectListView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen
|
||||
name='RoomInfoView'
|
||||
component={RoomInfoView}
|
||||
|
@ -174,6 +182,16 @@ const ChatsStackNavigator = () => {
|
|||
component={TeamChannelsView}
|
||||
options={TeamChannelsView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen
|
||||
name='AddChannelTeamView'
|
||||
component={AddChannelTeamView}
|
||||
options={AddChannelTeamView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen
|
||||
name='AddExistingChannelView'
|
||||
component={AddExistingChannelView}
|
||||
options={AddExistingChannelView.navigationOptions}
|
||||
/>
|
||||
<ChatsStack.Screen
|
||||
name='MarkdownTableView'
|
||||
component={MarkdownTableView}
|
||||
|
|
|
@ -8,17 +8,36 @@ const navigate = ({ item, isMasterDetail, ...props }) => {
|
|||
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
|
||||
});
|
||||
if (item.isTeamChannel) {
|
||||
// TODO: Refactor
|
||||
Navigation.navigate('TeamChannelsView');
|
||||
Navigation.push('RoomView', {
|
||||
rid: item.roomId || item.rid,
|
||||
name: RocketChat.getRoomTitle(item),
|
||||
t: item.type ? 'p' : item.t,
|
||||
prid: item.prid,
|
||||
room: item,
|
||||
search: item.search,
|
||||
visitor: item.visitor,
|
||||
roomUserId: RocketChat.getUidDirectMessage(item),
|
||||
teamId: item.teamId,
|
||||
...props
|
||||
});
|
||||
} else if (item.rooms) {
|
||||
Navigation.navigate('TeamChannelsView');
|
||||
} else {
|
||||
navigationMethod('RoomView', {
|
||||
rid: item.roomId || item.rid,
|
||||
name: RocketChat.getRoomTitle(item),
|
||||
t: item.type ? 'p' : 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 }) => {
|
||||
|
|
|
@ -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',
|
||||
|
@ -100,12 +101,16 @@ export default {
|
|||
|
||||
// CREATE CHANNEL VIEW
|
||||
CR_CREATE: 'cr_create',
|
||||
CT_CREATE: 'ct_create',
|
||||
CR_CREATE_F: 'cr_create_f',
|
||||
CT_CREATE_F: 'ct_create_f',
|
||||
CR_TOGGLE_TYPE: 'cr_toggle_type',
|
||||
CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
|
||||
CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
|
||||
CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
|
||||
CR_REMOVE_USER: 'cr_remove_user',
|
||||
CT_ADD_ROOM_TO_TEAM: 'ct_add_room_to_team',
|
||||
CT_ADD_ROOM_TO_TEAM_F: 'ct_add_room_to_team_f',
|
||||
|
||||
// CREATE DISCUSSION VIEW
|
||||
CD_CREATE: 'cd_create',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { RectButton } from 'react-native-gesture-handler';
|
||||
|
||||
import { Pressable } from 'react-native';
|
||||
import { themes } from '../constants/colors';
|
||||
|
||||
class Touch extends React.Component {
|
||||
|
@ -15,20 +15,21 @@ class Touch extends React.Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
children, onPress, theme, underlayColor, ...props
|
||||
children, onPress, onLongPress, theme, style, ...props
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<RectButton
|
||||
<Pressable
|
||||
ref={this.getRef}
|
||||
onPress={onPress}
|
||||
onLongPress={onLongPress}
|
||||
activeOpacity={1}
|
||||
underlayColor={underlayColor || themes[theme].bannerBackground}
|
||||
rippleColor={themes[theme].bannerBackground}
|
||||
style={style}
|
||||
android_ripple={{ color: themes[theme].bannerBackground }}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</RectButton>
|
||||
</Pressable>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -36,8 +37,10 @@ class Touch extends React.Component {
|
|||
Touch.propTypes = {
|
||||
children: PropTypes.node,
|
||||
onPress: PropTypes.func,
|
||||
onLongPress: PropTypes.func,
|
||||
theme: PropTypes.string,
|
||||
underlayColor: PropTypes.string
|
||||
underlayColor: PropTypes.string,
|
||||
style: PropTypes.object
|
||||
};
|
||||
|
||||
export default Touch;
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
import React from 'react';
|
||||
import { View, Text, StyleSheet } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { HeaderBackButton } from '@react-navigation/stack';
|
||||
|
||||
import sharedStyles from './Styles';
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import Touch from '../utils/touch';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { withTheme } from '../theme';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import { withDimensions } from '../dimensions';
|
||||
import { themes } from '../constants/colors';
|
||||
import I18n from '../i18n';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: 18,
|
||||
marginRight: 16
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
buttonContainer: {
|
||||
paddingVertical: 25
|
||||
}
|
||||
});
|
||||
|
||||
class AddChannelTeamView extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.teamId = props.route.params?.teamId;
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { navigation, isMasterDetail, theme } = this.props;
|
||||
|
||||
const options = {
|
||||
headerShown: true,
|
||||
headerTitleAlign: 'center',
|
||||
headerTitle: I18n.t('Add_Channel_to_Team')
|
||||
};
|
||||
|
||||
if (isMasterDetail) {
|
||||
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
|
||||
} else {
|
||||
options.headerLeft = () => (
|
||||
<HeaderBackButton
|
||||
labelVisible={false}
|
||||
onPress={() => navigation.pop()}
|
||||
tintColor={themes[theme].headerTintColor}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
navigation.setOptions(options);
|
||||
}
|
||||
|
||||
renderButton = ({
|
||||
onPress, testID, title, icon, first
|
||||
}) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={({ pressed }) => [{
|
||||
backgroundColor: pressed ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
|
||||
}]}
|
||||
testID={testID}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[first ? sharedStyles.separatorVertical : sharedStyles.separatorBottom, styles.button, { borderColor: themes[theme].separatorColor }]}>
|
||||
<CustomIcon style={[styles.buttonIcon, { color: themes[theme].tintColor }]} size={24} name={icon} />
|
||||
<Text style={[styles.buttonText, { color: themes[theme].tintColor }]}>{title}</Text>
|
||||
</View>
|
||||
</Touch>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { navigation, route } = this.props;
|
||||
const { teamChannels } = route?.params;
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='add-channel-team-view'>
|
||||
<StatusBar />
|
||||
<View style={styles.buttonContainer}>
|
||||
{this.renderButton({
|
||||
onPress: () => navigation.navigate('NewMessageStackNavigator', { screen: 'SelectedUsersViewCreateChannel', params: { nextAction: () => navigation.navigate('CreateChannelView', { teamId: this.teamId }) } }),
|
||||
title: I18n.t('Create_New'),
|
||||
icon: 'channel-public',
|
||||
testID: 'add-channel-team-view-create-channel',
|
||||
first: true
|
||||
})}
|
||||
{this.renderButton({
|
||||
onPress: () => navigation.navigate('AddExistingChannelView', { teamId: this.teamId, teamChannels }),
|
||||
title: I18n.t('Add_Existing'),
|
||||
icon: 'team',
|
||||
testID: 'add-channel-team-view-create-channel'
|
||||
})}
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddChannelTeamView.propTypes = {
|
||||
route: PropTypes.object,
|
||||
navigation: PropTypes.object,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default withDimensions(withTheme(AddChannelTeamView));
|
|
@ -0,0 +1,265 @@
|
|||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
View, StyleSheet, FlatList, Text
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { HeaderBackButton } from '@react-navigation/stack';
|
||||
import * as List from '../containers/List';
|
||||
|
||||
import Touch from '../utils/touch';
|
||||
import database from '../lib/database';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import sharedStyles from './Styles';
|
||||
import I18n from '../i18n';
|
||||
import log, { events, logEvent } from '../utils/log';
|
||||
import SearchBox from '../containers/SearchBox';
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { themes } from '../constants/colors';
|
||||
import { withTheme } from '../theme';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import { animateNextTransition } from '../utils/layoutAnimation';
|
||||
import { goRoom } from '../utils/goRoom';
|
||||
import Loading from '../containers/Loading';
|
||||
|
||||
const QUERY_SIZE = 15;
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: 18,
|
||||
marginRight: 16
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
textContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
marginRight: 15
|
||||
},
|
||||
icon: {
|
||||
marginHorizontal: 15,
|
||||
alignSelf: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
class AddExistingChannelView extends React.Component {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
}),
|
||||
theme: PropTypes.string,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
addTeamChannelPermission: PropTypes.array
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.init();
|
||||
this.teamId = props.route?.params?.teamId;
|
||||
this.state = {
|
||||
search: [],
|
||||
channels: [],
|
||||
selected: [],
|
||||
loading: false
|
||||
};
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { navigation, isMasterDetail, theme } = this.props;
|
||||
const { selected } = this.state;
|
||||
|
||||
const options = {
|
||||
headerShown: true,
|
||||
headerTitleAlign: 'center',
|
||||
headerTitle: I18n.t('Add_Existing_Channel')
|
||||
};
|
||||
|
||||
if (isMasterDetail) {
|
||||
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
|
||||
} else {
|
||||
options.headerLeft = () => <HeaderBackButton labelVisible={false} onPress={() => navigation.pop()} tintColor={themes[theme].headerTintColor} />;
|
||||
}
|
||||
|
||||
options.headerRight = () => selected.length > 0 && (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title={I18n.t('Create')} onPress={this.submit} testID='add-existing-channel-view-submit' />
|
||||
</HeaderButton.Container>
|
||||
);
|
||||
|
||||
navigation.setOptions(options);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/sort-comp
|
||||
init = async() => {
|
||||
try {
|
||||
const { addTeamChannelPermission } = this.props;
|
||||
const db = database.active;
|
||||
const channels = await db.collections
|
||||
.get('subscriptions')
|
||||
.query(
|
||||
Q.and(Q.where('team_id', ''), Q.or(Q.where('t', 'c'), Q.where('t', 'p'))),
|
||||
Q.experimentalTake(QUERY_SIZE),
|
||||
Q.experimentalSortBy('room_updated_at', Q.desc)
|
||||
)
|
||||
.fetch();
|
||||
const filteredChannels = channels.filter(async(channel) => {
|
||||
const permissions = await RocketChat.hasPermission([addTeamChannelPermission], channel.rid);
|
||||
if (!permissions[0]) {
|
||||
return;
|
||||
}
|
||||
return channel;
|
||||
});
|
||||
this.setState({ channels: filteredChannels });
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
onSearchChangeText(text) {
|
||||
this.search(text);
|
||||
}
|
||||
|
||||
dismiss = () => {
|
||||
const { navigation } = this.props;
|
||||
return navigation.pop();
|
||||
}
|
||||
|
||||
search = async(text) => {
|
||||
const result = await RocketChat.search({ text, filterUsers: false });
|
||||
this.setState({
|
||||
search: result
|
||||
});
|
||||
}
|
||||
|
||||
submit = async() => {
|
||||
const { selected } = this.state;
|
||||
const { isMasterDetail } = this.props;
|
||||
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
logEvent(events.CT_ADD_ROOM_TO_TEAM);
|
||||
const result = await RocketChat.addTeamRooms({ rooms: selected, teamId: this.teamId });
|
||||
if (result.success) {
|
||||
this.setState({ loading: false });
|
||||
goRoom({ item: result, isMasterDetail });
|
||||
}
|
||||
} catch (e) {
|
||||
logEvent(events.CT_ADD_ROOM_TO_TEAM_F);
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
renderChannel = ({
|
||||
onPress, testID, title, icon, checked
|
||||
}) => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
testID={testID}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[styles.button, { borderColor: themes[theme].separatorColor, marginVertical: 4 }]}>
|
||||
<CustomIcon style={[styles.buttonIcon, { color: themes[theme].controlText }]} size={24} name={icon} />
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.buttonText, { color: themes[theme].bodyText }]}>{title}</Text>
|
||||
</View>
|
||||
{checked ? <CustomIcon name={checked} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
||||
</View>
|
||||
</Touch>
|
||||
);
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].auxiliaryBackground }}>
|
||||
<SearchBox onChangeText={text => this.onSearchChangeText(text)} testID='add-existing-channel-view-search' />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
isChecked = (rid) => {
|
||||
const { selected } = this.state;
|
||||
return selected.includes(rid);
|
||||
}
|
||||
|
||||
toggleChannel = (rid) => {
|
||||
const { selected } = this.state;
|
||||
|
||||
animateNextTransition();
|
||||
if (!this.isChecked(rid)) {
|
||||
// logEvent(events.SELECTED_USERS_ADD_USER);
|
||||
this.setState({ selected: [...selected, rid] }, () => this.setHeader());
|
||||
} else {
|
||||
// logEvent(events.SELECTED_USERS_REMOVE_USER);
|
||||
const filterSelected = selected.filter(el => el !== rid);
|
||||
this.setState({ selected: filterSelected }, () => this.setHeader());
|
||||
}
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => (
|
||||
<>
|
||||
{this.renderChannel({
|
||||
onPress: () => this.toggleChannel(item.rid),
|
||||
title: item.name,
|
||||
icon: item.t === 'p' && !item.teamId ? 'channel-private' : 'channel-public',
|
||||
checked: this.isChecked(item.rid) ? 'check' : null,
|
||||
testID: 'add-existing-channel-view-item'
|
||||
})}
|
||||
</>
|
||||
)
|
||||
|
||||
renderList = () => {
|
||||
const { search, channels } = this.state;
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<FlatList
|
||||
data={search.length > 0 ? search : channels}
|
||||
extraData={this.state}
|
||||
keyExtractor={item => item._id}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
renderItem={this.renderItem}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading } = this.state;
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='new-message-view'>
|
||||
<StatusBar />
|
||||
{this.renderList()}
|
||||
<Loading visible={loading} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
addTeamChannelPermission: state.permissions['add-team-channel']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null)(withTheme(AddExistingChannelView));
|
|
@ -69,11 +69,12 @@ const styles = StyleSheet.create({
|
|||
|
||||
class CreateChannelView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
title: I18n.t('Create_Channel')
|
||||
});
|
||||
title: this.isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
|
||||
})
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
baseUrl: PropTypes.string,
|
||||
create: PropTypes.func.isRequired,
|
||||
removeUser: PropTypes.func.isRequired,
|
||||
|
@ -86,15 +87,22 @@ class CreateChannelView extends React.Component {
|
|||
id: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
}),
|
||||
theme: PropTypes.string
|
||||
theme: PropTypes.string,
|
||||
teamId: PropTypes.string
|
||||
};
|
||||
|
||||
state = {
|
||||
channelName: '',
|
||||
type: true,
|
||||
readOnly: false,
|
||||
encrypted: false,
|
||||
broadcast: false
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const { route } = this.props;
|
||||
this.isTeam = route?.params?.isTeam || false;
|
||||
this.teamId = route?.params?.teamId;
|
||||
this.state = {
|
||||
channelName: '',
|
||||
type: true,
|
||||
readOnly: false,
|
||||
encrypted: false,
|
||||
broadcast: false
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
@ -154,7 +162,9 @@ class CreateChannelView extends React.Component {
|
|||
const {
|
||||
channelName, type, readOnly, broadcast, encrypted
|
||||
} = this.state;
|
||||
const { users: usersProps, isFetching, create } = this.props;
|
||||
const {
|
||||
users: usersProps, isFetching, create
|
||||
} = this.props;
|
||||
|
||||
if (!channelName.trim() || isFetching) {
|
||||
return;
|
||||
|
@ -163,9 +173,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: this.isTeam, teamId: this.teamId
|
||||
});
|
||||
|
||||
Review.pushPositiveEvent();
|
||||
|
@ -197,10 +207,11 @@ class CreateChannelView extends React.Component {
|
|||
|
||||
renderType() {
|
||||
const { type } = this.state;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'type',
|
||||
value: type,
|
||||
label: 'Private_Channel',
|
||||
label: this.isTeam ? 'Private_Team' : 'Private_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CR_TOGGLE_TYPE);
|
||||
// If we set the channel as public, encrypted status should be false
|
||||
|
@ -211,10 +222,11 @@ class CreateChannelView extends React.Component {
|
|||
|
||||
renderReadOnly() {
|
||||
const { readOnly, broadcast } = this.state;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'readonly',
|
||||
value: readOnly,
|
||||
label: 'Read_Only_Channel',
|
||||
label: this.isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CR_TOGGLE_READ_ONLY);
|
||||
this.setState({ readOnly: value });
|
||||
|
@ -245,10 +257,11 @@ class CreateChannelView extends React.Component {
|
|||
|
||||
renderBroadcast() {
|
||||
const { broadcast, readOnly } = this.state;
|
||||
|
||||
return this.renderSwitch({
|
||||
id: 'broadcast',
|
||||
value: broadcast,
|
||||
label: 'Broadcast_Channel',
|
||||
label: this.isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
|
||||
onValueChange: (value) => {
|
||||
logEvent(events.CR_TOGGLE_BROADCAST);
|
||||
this.setState({
|
||||
|
@ -302,7 +315,9 @@ class CreateChannelView extends React.Component {
|
|||
|
||||
render() {
|
||||
const { channelName } = this.state;
|
||||
const { users, isFetching, theme } = this.props;
|
||||
const {
|
||||
users, isFetching, theme
|
||||
} = this.props;
|
||||
const userCount = users.length;
|
||||
|
||||
return (
|
||||
|
@ -312,18 +327,18 @@ class CreateChannelView extends React.Component {
|
|||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<StatusBar />
|
||||
<SafeAreaView testID='create-channel-view'>
|
||||
<SafeAreaView testID={this.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={this.isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
|
||||
value={channelName}
|
||||
onChangeText={this.onChangeText}
|
||||
placeholder={I18n.t('Channel_Name')}
|
||||
placeholder={this.isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
|
||||
returnKeyType='done'
|
||||
testID='create-channel-name'
|
||||
testID={this.isTeam ? 'create-team-name' : 'create-channel-name'}
|
||||
autoCorrect={false}
|
||||
autoCapitalize='none'
|
||||
theme={theme}
|
||||
|
|
|
@ -67,7 +67,7 @@ export default class DirectoryOptions extends PureComponent {
|
|||
return (
|
||||
<Touch
|
||||
onPress={() => changeType(itemType)}
|
||||
style={styles.dropdownItemButton}
|
||||
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }, styles.dropdownItemButton]}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={styles.dropdownItemContainer}>
|
||||
|
|
|
@ -169,7 +169,7 @@ class DirectoryView extends React.Component {
|
|||
/>
|
||||
<Touch
|
||||
onPress={this.toggleDropdown}
|
||||
style={styles.dropdownItemButton}
|
||||
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }, styles.dropdownItemButton]}
|
||||
testID='directory-view-dropdown'
|
||||
theme={theme}
|
||||
>
|
||||
|
|
|
@ -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;
|
||||
|
@ -142,7 +148,9 @@ class NewMessageView extends React.Component {
|
|||
return (
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
style={({ pressed }) => [{
|
||||
backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor
|
||||
}]}
|
||||
testID={testID}
|
||||
theme={theme}
|
||||
>
|
||||
|
@ -172,6 +180,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: 'team',
|
||||
testID: 'new-message-view-create-team'
|
||||
})}
|
||||
{maxUsers > 2 ? this.renderButton({
|
||||
onPress: this.createGroupChat,
|
||||
title: I18n.t('Create_Direct_Messages'),
|
||||
|
@ -253,7 +267,7 @@ const mapStateToProps = state => ({
|
|||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
createChannel: params => dispatch(createChannelRequest(params))
|
||||
create: params => dispatch(createChannelRequest(params))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
|
||||
|
|
|
@ -309,7 +309,7 @@ class ProfileView extends React.Component {
|
|||
key={key}
|
||||
testID={key}
|
||||
onPress={onPress}
|
||||
style={[styles.avatarButton, { opacity: disabled ? 0.5 : 1 }, { backgroundColor: themes[theme].borderColor }]}
|
||||
style={({ pressed }) => [styles.avatarButton, { opacity: disabled ? 0.5 : 1 }, { backgroundColor: pressed ? themes[theme].borderColor : themes[theme].bannerBackground }]}
|
||||
enabled={!disabled}
|
||||
theme={theme}
|
||||
>
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { compareServerVersion, methods } from '../../lib/utils';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
|
@ -60,7 +61,8 @@ class RoomActionsView extends React.Component {
|
|||
editRoomPermission: PropTypes.array,
|
||||
toggleRoomE2EEncryptionPermission: PropTypes.array,
|
||||
viewBroadcastMemberListPermission: PropTypes.array,
|
||||
transferLivechatGuestPermission: PropTypes.array
|
||||
transferLivechatGuestPermission: PropTypes.array,
|
||||
addTeamMemberPermission: PropTypes.array
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -170,12 +172,19 @@ class RoomActionsView extends React.Component {
|
|||
|
||||
canAddUser = async() => {
|
||||
const { room, joined } = this.state;
|
||||
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
|
||||
const {
|
||||
addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission, addTeamMemberPermission
|
||||
} = this.props;
|
||||
const { rid, t } = room;
|
||||
let canAddUser = false;
|
||||
let permissions;
|
||||
|
||||
const userInRoom = joined;
|
||||
const permissions = await RocketChat.hasPermission([addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission], rid);
|
||||
if (room.teamMain) {
|
||||
permissions = await RocketChat.hasPermission([addTeamMemberPermission], rid);
|
||||
} else {
|
||||
permissions = await RocketChat.hasPermission([addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission], rid);
|
||||
}
|
||||
|
||||
if (userInRoom && permissions[0]) {
|
||||
canAddUser = true;
|
||||
|
@ -320,6 +329,31 @@ class RoomActionsView extends React.Component {
|
|||
setLoadingInvite(true);
|
||||
await RocketChat.addUsersToRoom(rid);
|
||||
navigation.pop();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
Alert.alert(
|
||||
I18n.t('Confirmation'),
|
||||
I18n.t('Removing_user_from_this_Team'),
|
||||
[
|
||||
{
|
||||
text: I18n.t('OK'),
|
||||
style: 'cancel'
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
||||
} finally {
|
||||
setLoadingInvite(false);
|
||||
}
|
||||
}
|
||||
|
||||
addMemberToTeam = async() => {
|
||||
const { room } = this.state;
|
||||
const { setLoadingInvite, navigation } = this.props;
|
||||
try {
|
||||
setLoadingInvite(true);
|
||||
await RocketChat.addTeamMember(room.name);
|
||||
navigation.pop();
|
||||
} catch (e) {
|
||||
log(e);
|
||||
} finally {
|
||||
|
@ -412,6 +446,56 @@ class RoomActionsView extends React.Component {
|
|||
);
|
||||
}
|
||||
|
||||
_leave = async(teamName) => {
|
||||
try {
|
||||
const { navigation } = this.props;
|
||||
const result = await RocketChat.leaveTeam({ teamName });
|
||||
// Add isMasterDetail
|
||||
if (result.success) {
|
||||
navigation.navigate('RoomsListView');
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
leaveTeam = async() => {
|
||||
const { room } = this.state;
|
||||
const { navigation } = this.props;
|
||||
|
||||
try {
|
||||
const db = database.active;
|
||||
const subCollection = db.get('subscriptions');
|
||||
const teamChannels = await subCollection.query(
|
||||
Q.where('team_id', Q.eq(room.teamId))
|
||||
);
|
||||
|
||||
if (teamChannels) {
|
||||
navigation.navigate('SelectListView', {
|
||||
title: 'Leave_Team', room, teamChannels, teamName: room.name, subtitle: 'Select_Teams'
|
||||
});
|
||||
} else {
|
||||
Alert.alert(
|
||||
I18n.t('Confirmation'),
|
||||
I18n.t('You_are_leaving_the_team', { team: RocketChat.getRoomTitle(room) }),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
|
||||
style: 'destructive',
|
||||
onPress: () => this._leave(room.name)
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
renderRoomInfo = () => {
|
||||
const { room, member } = this.state;
|
||||
const {
|
||||
|
@ -433,7 +517,7 @@ class RoomActionsView extends React.Component {
|
|||
rid, t, room, member
|
||||
}
|
||||
})}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }]}
|
||||
accessibilityLabel={I18n.t('Room_Info')}
|
||||
accessibilityTraits='button'
|
||||
enabled={!isGroupChat}
|
||||
|
@ -568,9 +652,9 @@ class RoomActionsView extends React.Component {
|
|||
<List.Section>
|
||||
<List.Separator />
|
||||
<List.Item
|
||||
title='Leave_channel'
|
||||
title={room.teamId && room.teamMain ? 'Leave' : 'Leave_channel'}
|
||||
onPress={() => this.onPressTouchable({
|
||||
event: this.leaveChannel
|
||||
event: room.teamId && room.teamMain ? this.leaveTeam : this.leaveChannel
|
||||
})}
|
||||
testID='room-actions-leave-channel'
|
||||
left={() => <List.Icon name='logout' color={themes[theme].dangerColor} />}
|
||||
|
@ -629,7 +713,7 @@ class RoomActionsView extends React.Component {
|
|||
params: {
|
||||
rid,
|
||||
title: I18n.t('Add_users'),
|
||||
nextAction: this.addUser
|
||||
nextAction: room.teamId ? this.addMemberToTeam : this.addUser
|
||||
}
|
||||
})}
|
||||
testID='room-actions-add-user'
|
||||
|
@ -881,6 +965,7 @@ const mapStateToProps = state => ({
|
|||
encryptionEnabled: state.encryption.enabled,
|
||||
serverVersion: state.server.version,
|
||||
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
||||
addTeamMemberPermission: state.permissions['add-team-member'],
|
||||
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
||||
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
||||
createInviteLinksPermission: state.permissions['create-invite-links'],
|
||||
|
|
|
@ -8,6 +8,7 @@ import { BLOCK_CONTEXT } from '@rocket.chat/ui-kit';
|
|||
import ImagePicker from 'react-native-image-crop-picker';
|
||||
import { dequal } from 'dequal';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { compareServerVersion, methods } from '../../lib/utils';
|
||||
|
||||
import database from '../../lib/database';
|
||||
|
@ -41,6 +42,7 @@ const PERMISSION_ARCHIVE = 'archive-room';
|
|||
const PERMISSION_UNARCHIVE = 'unarchive-room';
|
||||
const PERMISSION_DELETE_C = 'delete-c';
|
||||
const PERMISSION_DELETE_P = 'delete-p';
|
||||
const PERMISSION_EDIT_TEAM_CHANNEL = 'edit-team-channel';
|
||||
|
||||
class RoomInfoEditView extends React.Component {
|
||||
static navigationOptions = () => ({
|
||||
|
@ -48,6 +50,7 @@ class RoomInfoEditView extends React.Component {
|
|||
})
|
||||
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
deleteRoom: PropTypes.func,
|
||||
serverVersion: PropTypes.string,
|
||||
|
@ -58,7 +61,8 @@ class RoomInfoEditView extends React.Component {
|
|||
archiveRoomPermission: PropTypes.array,
|
||||
unarchiveRoomPermission: PropTypes.array,
|
||||
deleteCPermission: PropTypes.array,
|
||||
deletePPermission: PropTypes.array
|
||||
deletePPermission: PropTypes.array,
|
||||
editTeamChannelPermission: PropTypes.array
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -100,7 +104,8 @@ class RoomInfoEditView extends React.Component {
|
|||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission
|
||||
deletePPermission,
|
||||
editTeamChannelPermission
|
||||
} = this.props;
|
||||
const rid = route.params?.rid;
|
||||
if (!rid) {
|
||||
|
@ -116,25 +121,51 @@ class RoomInfoEditView extends React.Component {
|
|||
this.init(this.room);
|
||||
});
|
||||
|
||||
const result = await RocketChat.hasPermission([
|
||||
setReadOnlyPermission,
|
||||
setReactWhenReadOnlyPermission,
|
||||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission
|
||||
], rid);
|
||||
let result;
|
||||
|
||||
this.setState({
|
||||
permissions: {
|
||||
[PERMISSION_SET_READONLY]: result[0],
|
||||
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
|
||||
[PERMISSION_ARCHIVE]: result[2],
|
||||
[PERMISSION_UNARCHIVE]: result[3],
|
||||
[PERMISSION_DELETE_C]: result[4],
|
||||
[PERMISSION_DELETE_P]: result[5]
|
||||
}
|
||||
});
|
||||
if (this.room.teamId) {
|
||||
result = await RocketChat.hasPermission([
|
||||
setReadOnlyPermission,
|
||||
setReactWhenReadOnlyPermission,
|
||||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission,
|
||||
editTeamChannelPermission
|
||||
], rid);
|
||||
|
||||
this.setState({
|
||||
permissions: {
|
||||
[PERMISSION_SET_READONLY]: result[0],
|
||||
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
|
||||
[PERMISSION_ARCHIVE]: result[2],
|
||||
[PERMISSION_UNARCHIVE]: result[3],
|
||||
[PERMISSION_DELETE_C]: result[4],
|
||||
[PERMISSION_DELETE_P]: result[5],
|
||||
[PERMISSION_EDIT_TEAM_CHANNEL]: result[6]
|
||||
}
|
||||
});
|
||||
} else {
|
||||
result = await RocketChat.hasPermission([
|
||||
setReadOnlyPermission,
|
||||
setReactWhenReadOnlyPermission,
|
||||
archiveRoomPermission,
|
||||
unarchiveRoomPermission,
|
||||
deleteCPermission,
|
||||
deletePPermission
|
||||
], rid);
|
||||
|
||||
this.setState({
|
||||
permissions: {
|
||||
[PERMISSION_SET_READONLY]: result[0],
|
||||
[PERMISSION_SET_REACT_WHEN_READONLY]: result[1],
|
||||
[PERMISSION_ARCHIVE]: result[2],
|
||||
[PERMISSION_UNARCHIVE]: result[3],
|
||||
[PERMISSION_DELETE_C]: result[4],
|
||||
[PERMISSION_DELETE_P]: result[5]
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
|
@ -284,13 +315,37 @@ class RoomInfoEditView extends React.Component {
|
|||
}, 100);
|
||||
}
|
||||
|
||||
deleteTeam = async(teamName) => {
|
||||
const { navigation } = this.props;
|
||||
const { room } = this.state;
|
||||
try {
|
||||
const result = await RocketChat.deleteTeam({ teamName });
|
||||
if (result.success) {
|
||||
const db = database.active;
|
||||
const subCollection = db.get('subscriptions');
|
||||
const teamChannels = await subCollection.query(
|
||||
Q.and(Q.where('team_id', Q.eq(this.room.teamId), Q.where('name', Q.notEq(this.room.name))))
|
||||
);
|
||||
if (teamChannels.length) {
|
||||
navigation.navigate('SelectListView', {
|
||||
title: 'Delete_Team', teamChannels: this.teamChannels, teamName: room.name, subtitle: 'Select_Teams', delete: this.delete
|
||||
});
|
||||
} else {
|
||||
navigation.navigate('RoomsListView');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
delete = () => {
|
||||
const { room } = this.state;
|
||||
const { deleteRoom } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
I18n.t('Are_you_sure_question_mark'),
|
||||
I18n.t('Delete_Room_Warning'),
|
||||
I18n.t('Confirmation'),
|
||||
I18n.t('Delete_Team_Warning'),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
|
@ -299,7 +354,7 @@ class RoomInfoEditView extends React.Component {
|
|||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
|
||||
style: 'destructive',
|
||||
onPress: () => deleteRoom(room.rid, room.t)
|
||||
onPress: () => (this.room.teamId ? this.deleteTeam(room.name) : deleteRoom(room.rid, room.t))
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
|
@ -678,7 +733,8 @@ const mapStateToProps = state => ({
|
|||
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
|
||||
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
|
||||
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
|
||||
deletePPermission: state.permissions[PERMISSION_DELETE_P]
|
||||
deletePPermission: state.permissions[PERMISSION_DELETE_P],
|
||||
editTeamChannelPermission: state.permissions[PERMISSION_EDIT_TEAM_CHANNEL]
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FlatList } from 'react-native';
|
||||
import { FlatList, Alert } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import * as List from '../../containers/List';
|
||||
|
@ -34,6 +34,7 @@ const PERMISSION_SET_LEADER = 'set-leader';
|
|||
const PERMISSION_SET_OWNER = 'set-owner';
|
||||
const PERMISSION_SET_MODERATOR = 'set-moderator';
|
||||
const PERMISSION_REMOVE_USER = 'remove-user';
|
||||
const PERMISSION_EDIT_TEAM_MEMBER = 'edit-team-member';
|
||||
|
||||
class RoomMembersView extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -55,7 +56,8 @@ class RoomMembersView extends React.Component {
|
|||
setLeaderPermission: PropTypes.array,
|
||||
setOwnerPermission: PropTypes.array,
|
||||
setModeratorPermission: PropTypes.array,
|
||||
removeUserPermission: PropTypes.array
|
||||
removeUserPermission: PropTypes.array,
|
||||
editTeamMemberPermission: PropTypes.array
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
|
@ -94,18 +96,44 @@ class RoomMembersView extends React.Component {
|
|||
|
||||
const { room } = this.state;
|
||||
const {
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, editTeamMemberPermission
|
||||
} = this.props;
|
||||
const result = await RocketChat.hasPermission([
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||
], room.rid);
|
||||
let result;
|
||||
|
||||
if (room.teamId) {
|
||||
result = await RocketChat.hasPermission([
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, editTeamMemberPermission
|
||||
], room.rid);
|
||||
|
||||
this.permissions = {
|
||||
[PERMISSION_MUTE_USER]: result[0],
|
||||
[PERMISSION_SET_LEADER]: result[1],
|
||||
[PERMISSION_SET_OWNER]: result[2],
|
||||
[PERMISSION_SET_MODERATOR]: result[3],
|
||||
[PERMISSION_REMOVE_USER]: result[4],
|
||||
[PERMISSION_EDIT_TEAM_MEMBER]: result[5]
|
||||
};
|
||||
} else {
|
||||
result = await RocketChat.hasPermission([
|
||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||
], room.rid);
|
||||
|
||||
this.permissions = {
|
||||
[PERMISSION_MUTE_USER]: result[0],
|
||||
[PERMISSION_SET_LEADER]: result[1],
|
||||
[PERMISSION_SET_OWNER]: result[2],
|
||||
[PERMISSION_SET_MODERATOR]: result[3],
|
||||
[PERMISSION_REMOVE_USER]: result[4]
|
||||
};
|
||||
}
|
||||
|
||||
this.permissions = {
|
||||
[PERMISSION_MUTE_USER]: result[0],
|
||||
[PERMISSION_SET_LEADER]: result[1],
|
||||
[PERMISSION_SET_OWNER]: result[2],
|
||||
[PERMISSION_SET_MODERATOR]: result[3],
|
||||
[PERMISSION_REMOVE_USER]: result[4]
|
||||
[PERMISSION_REMOVE_USER]: result[4],
|
||||
[PERMISSION_EDIT_TEAM_MEMBER]: result[5]
|
||||
};
|
||||
|
||||
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
||||
|
@ -163,6 +191,56 @@ class RoomMembersView extends React.Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleRemoveFromTeam = async(selectedUser) => {
|
||||
const { navigation } = this.props;
|
||||
const { room } = this.state;
|
||||
const db = database.active;
|
||||
const subCollection = db.get('subscriptions');
|
||||
const teamChannels = await subCollection.query(
|
||||
Q.where('team_id', Q.eq(room.teamId))
|
||||
);
|
||||
if (teamChannels) {
|
||||
navigation.navigate('SelectListView', {
|
||||
title: 'Remove_Member', subtitle: 'Remove_User_Teams', teamChannels, selectedUser
|
||||
});
|
||||
} else {
|
||||
Alert.alert(
|
||||
I18n.t('Confirmation'),
|
||||
I18n.t('Removing_user_from_this_Team', { user: selectedUser.username }),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
style: 'destructive',
|
||||
onPress: () => this.removeFromTeam(selectedUser)
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removeFromTeam = async(selectedUser) => {
|
||||
try {
|
||||
const { members, membersFiltered, room } = this.state;
|
||||
const userId = selectedUser._id;
|
||||
const result = await RocketChat.removeTeamMember({ teamName: room.name, userId });
|
||||
if (result.success) {
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: RocketChat.getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
this.setState({
|
||||
members: members.filter(member => member._id !== userId),
|
||||
membersFiltered: membersFiltered.filter(member => member._id !== userId)
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
onPressUser = (selectedUser) => {
|
||||
const { room } = this.state;
|
||||
const { showActionSheet, user } = this.props;
|
||||
|
@ -173,6 +251,46 @@ class RoomMembersView extends React.Component {
|
|||
onPress: () => this.navToDirectMessage(selectedUser)
|
||||
}];
|
||||
|
||||
// Ignore
|
||||
if (selectedUser._id !== user.id) {
|
||||
const { ignored } = room;
|
||||
const isIgnored = ignored?.includes?.(selectedUser._id);
|
||||
options.push({
|
||||
icon: 'ignore',
|
||||
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||
onPress: () => this.handleIgnore(selectedUser, !isIgnored)
|
||||
});
|
||||
}
|
||||
|
||||
if (this.permissions['mute-user']) {
|
||||
const { muted = [] } = room;
|
||||
const userIsMuted = muted.find?.(m => m === selectedUser.username);
|
||||
selectedUser.muted = !!userIsMuted;
|
||||
options.push({
|
||||
icon: userIsMuted ? 'audio' : 'audio-disabled',
|
||||
title: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||
onPress: () => {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t(`The_user_${ userIsMuted ? 'will' : 'wont' }_be_able_to_type_in_roomName`, {
|
||||
roomName: RocketChat.getRoomTitle(room)
|
||||
}),
|
||||
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||
onPress: () => this.handleMute(selectedUser)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove from team
|
||||
if (this.permissions['edit-team-member']) {
|
||||
options.push({
|
||||
icon: 'close',
|
||||
danger: true,
|
||||
title: I18n.t('Remove_from_Team'),
|
||||
onPress: () => this.handleRemoveFromTeam(selectedUser)
|
||||
});
|
||||
}
|
||||
|
||||
// Owner
|
||||
if (this.permissions['set-owner']) {
|
||||
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
|
||||
|
@ -206,36 +324,6 @@ class RoomMembersView extends React.Component {
|
|||
});
|
||||
}
|
||||
|
||||
// Ignore
|
||||
if (selectedUser._id !== user.id) {
|
||||
const { ignored } = room;
|
||||
const isIgnored = ignored?.includes?.(selectedUser._id);
|
||||
options.push({
|
||||
icon: 'ignore',
|
||||
title: I18n.t(isIgnored ? 'Unignore' : 'Ignore'),
|
||||
onPress: () => this.handleIgnore(selectedUser, !isIgnored)
|
||||
});
|
||||
}
|
||||
|
||||
if (this.permissions['mute-user']) {
|
||||
const { muted = [] } = room;
|
||||
const userIsMuted = muted.find?.(m => m === selectedUser.username);
|
||||
selectedUser.muted = !!userIsMuted;
|
||||
options.push({
|
||||
icon: userIsMuted ? 'audio' : 'audio-disabled',
|
||||
title: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||
onPress: () => {
|
||||
showConfirmationAlert({
|
||||
message: I18n.t(`The_user_${ userIsMuted ? 'will' : 'wont' }_be_able_to_type_in_roomName`, {
|
||||
roomName: RocketChat.getRoomTitle(room)
|
||||
}),
|
||||
confirmationText: I18n.t(userIsMuted ? 'Unmute' : 'Mute'),
|
||||
onPress: () => this.handleMute(selectedUser)
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Remove from room
|
||||
if (this.permissions['remove-user']) {
|
||||
options.push({
|
||||
|
@ -292,6 +380,7 @@ class RoomMembersView extends React.Component {
|
|||
this.setState({ isLoading: true });
|
||||
try {
|
||||
const membersResult = await RocketChat.getRoomMembers(rid, allUsers, members.length, PAGE_SIZE);
|
||||
console.log({ membersResult });
|
||||
const newMembers = membersResult.records;
|
||||
this.setState({
|
||||
members: members.concat(newMembers || []),
|
||||
|
@ -477,7 +566,8 @@ const mapStateToProps = state => ({
|
|||
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
|
||||
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
|
||||
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
|
||||
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER]
|
||||
removeUserPermission: state.permissions[PERMISSION_REMOVE_USER],
|
||||
editTeamMemberPermission: state.permissions['edit-team-member']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));
|
||||
|
|
|
@ -18,7 +18,8 @@ class RightButtonsContainer extends Component {
|
|||
teamId: PropTypes.bool,
|
||||
navigation: PropTypes.object,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
toggleFollowThread: PropTypes.func
|
||||
toggleFollowThread: PropTypes.func,
|
||||
joined: PropTypes.bool
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -113,7 +114,7 @@ class RightButtonsContainer extends Component {
|
|||
goTeamChannels = () => {
|
||||
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
||||
const {
|
||||
navigation, isMasterDetail, teamId
|
||||
navigation, isMasterDetail, teamId, rid
|
||||
} = this.props;
|
||||
if (isMasterDetail) {
|
||||
navigation.navigate('ModalStackNavigator', {
|
||||
|
@ -121,7 +122,7 @@ class RightButtonsContainer extends Component {
|
|||
params: { teamId }
|
||||
});
|
||||
} else {
|
||||
navigation.navigate('TeamChannelsView', { teamId });
|
||||
navigation.navigate('TeamChannelsView', { teamId, rid });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,7 +164,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;
|
||||
|
@ -181,7 +182,7 @@ class RightButtonsContainer extends Component {
|
|||
}
|
||||
return (
|
||||
<HeaderButton.Container>
|
||||
{teamId ? (
|
||||
{teamId && joined ? (
|
||||
<HeaderButton.Item
|
||||
iconName='channel-public'
|
||||
onPress={this.goTeamChannels}
|
||||
|
|
|
@ -301,7 +301,7 @@ class RoomView extends React.Component {
|
|||
|
||||
setHeader = () => {
|
||||
const {
|
||||
room, unreadsCount, roomUserId
|
||||
room, unreadsCount, roomUserId, joined
|
||||
} = this.state;
|
||||
const {
|
||||
navigation, isMasterDetail, theme, baseUrl, user, insets, route
|
||||
|
@ -331,7 +331,7 @@ class RoomView extends React.Component {
|
|||
let numIconsRight = 2;
|
||||
if (tmid) {
|
||||
numIconsRight = 1;
|
||||
} else if (teamId) {
|
||||
} else if (teamId && joined) {
|
||||
numIconsRight = 3;
|
||||
}
|
||||
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight });
|
||||
|
@ -380,6 +380,7 @@ class RoomView extends React.Component {
|
|||
rid={rid}
|
||||
tmid={tmid}
|
||||
teamId={teamId}
|
||||
joined={joined}
|
||||
t={t}
|
||||
navigation={navigation}
|
||||
toggleFollowThread={this.toggleFollowThread}
|
||||
|
@ -1032,6 +1033,7 @@ class RoomView extends React.Component {
|
|||
renderActions = () => {
|
||||
const { room, readOnly } = this.state;
|
||||
const { user } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<MessageActions
|
||||
|
|
|
@ -894,7 +894,6 @@ class RoomsListView extends React.Component {
|
|||
return this.renderSectionHeader(item.rid);
|
||||
}
|
||||
|
||||
const { item: currentItem } = this.state;
|
||||
const {
|
||||
user: { username },
|
||||
StoreLastMessage,
|
||||
|
@ -925,7 +924,6 @@ class RoomsListView extends React.Component {
|
|||
getIsGroupChat={this.isGroupChat}
|
||||
getIsRead={this.isRead}
|
||||
visitor={item.visitor}
|
||||
isFocused={currentItem?.rid === item.rid}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -948,7 +946,6 @@ class RoomsListView extends React.Component {
|
|||
if (loading) {
|
||||
return <ActivityIndicator theme={theme} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
ref={this.getScrollRef}
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/* eslint-disable no-mixed-spaces-and-tabs */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
View, StyleSheet, FlatList, Text, Alert, Pressable
|
||||
} from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { HeaderBackButton } from '@react-navigation/stack';
|
||||
import * as List from '../containers/List';
|
||||
|
||||
import Touch from '../utils/touch';
|
||||
import { leaveRoom as leaveRoomAction } from '../actions/room';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
import sharedStyles from './Styles';
|
||||
import I18n from '../i18n';
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import * as HeaderButton from '../containers/HeaderButton';
|
||||
import StatusBar from '../containers/StatusBar';
|
||||
import { themes } from '../constants/colors';
|
||||
import { withTheme } from '../theme';
|
||||
import SafeAreaView from '../containers/SafeAreaView';
|
||||
import { animateNextTransition } from '../utils/layoutAnimation';
|
||||
import Loading from '../containers/Loading';
|
||||
import { LISTENER } from '../containers/Toast';
|
||||
import EventEmitter from '../utils/events';
|
||||
import log from '../utils/log';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
button: {
|
||||
height: 46,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
buttonIcon: {
|
||||
marginLeft: 18,
|
||||
marginRight: 16
|
||||
},
|
||||
buttonText: {
|
||||
fontSize: 17,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
textContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 15
|
||||
},
|
||||
icon: {
|
||||
marginHorizontal: 15,
|
||||
alignSelf: 'center'
|
||||
}
|
||||
});
|
||||
|
||||
class SelectListView extends React.Component {
|
||||
static propTypes = {
|
||||
navigation: PropTypes.object,
|
||||
route: PropTypes.object,
|
||||
user: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
token: PropTypes.string
|
||||
}),
|
||||
theme: PropTypes.string,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
leaveRoom: PropTypes.func
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const teamChannels = props.route?.params?.teamChannels;
|
||||
this.title = props.route?.params?.title;
|
||||
this.subtitle = props.route?.params?.subtitle;
|
||||
this.teamName = props.route?.params?.teamName;
|
||||
this.room = props.route?.params?.room;
|
||||
this.delete = props.route?.params?.delete;
|
||||
this.state = {
|
||||
data: teamChannels,
|
||||
selected: [],
|
||||
loading: false
|
||||
};
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { navigation, isMasterDetail, theme } = this.props;
|
||||
|
||||
const options = {
|
||||
headerShown: true,
|
||||
headerTitleAlign: 'center',
|
||||
headerTitle: I18n.t(this.title)
|
||||
};
|
||||
|
||||
if (isMasterDetail) {
|
||||
options.headerLeft = () => <HeaderButton.CloseModal navigation={navigation} />;
|
||||
} else {
|
||||
options.headerLeft = () => <HeaderBackButton labelVisible={false} onPress={() => navigation.pop()} tintColor={themes[theme].headerTintColor} />;
|
||||
}
|
||||
|
||||
options.headerRight = () => (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item title={I18n.t('Next')} onPress={this.delete ? this.delete : this.submit} testID='select-list-view-submit' />
|
||||
</HeaderButton.Container>
|
||||
);
|
||||
|
||||
navigation.setOptions(options);
|
||||
}
|
||||
|
||||
submit = async() => {
|
||||
const { selected } = this.state;
|
||||
const { navigation, leaveRoom } = this.props;
|
||||
|
||||
this.setState({ loading: true });
|
||||
try {
|
||||
// logEvent(events.CT_ADD_ROOM_TO_TEAM);
|
||||
const result = await RocketChat.leaveTeam({ teamName: this.teamName });
|
||||
if (selected) {
|
||||
selected.map(room => leaveRoom(room.rid, room.t));
|
||||
}
|
||||
if (result.success) {
|
||||
this.setState({ loading: false });
|
||||
navigation.navigate('RoomsListView');
|
||||
}
|
||||
} catch (e) {
|
||||
// logEvent(events.CT_ADD_ROOM_TO_TEAM_F);
|
||||
this.setState({ loading: false });
|
||||
Alert.alert(
|
||||
I18n.t('Cannot_leave'),
|
||||
I18n.t(e.data.error),
|
||||
[
|
||||
{
|
||||
text: 'OK',
|
||||
style: 'cancel'
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
renderHeader = () => {
|
||||
const { theme } = this.props;
|
||||
return (
|
||||
<View style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<Text style={[styles.buttonText, { color: themes[theme].bodyText, margin: 16 }]}>{I18n.t('Select_Teams')}</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
showAlert = () => {
|
||||
Alert.alert(
|
||||
I18n.t('Cannot_leave'),
|
||||
I18n.t('Last_owner_team_room'),
|
||||
[
|
||||
{
|
||||
text: 'OK',
|
||||
style: 'cancel'
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
leaveChannel = () => {
|
||||
const { room } = this.state;
|
||||
const { leaveRoom } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
I18n.t('Are_you_sure_question_mark'),
|
||||
I18n.t('Are_you_sure_you_want_to_leave_the_room', { room: RocketChat.getRoomTitle(room) }),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('leave') }),
|
||||
style: 'destructive',
|
||||
onPress: () => leaveRoom(room.rid, room.t)
|
||||
}
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
removeFromTeam = async(selectedUser) => {
|
||||
try {
|
||||
const { data, room } = this.state;
|
||||
const userId = selectedUser._id;
|
||||
const result = await RocketChat.removeTeamMember({ teamName: room.name, userId });
|
||||
if (result.success) {
|
||||
const message = I18n.t('User_has_been_removed_from_s', { s: RocketChat.getRoomTitle(room) });
|
||||
EventEmitter.emit(LISTENER, { message });
|
||||
this.setState({ data: data.filter(member => member._id !== userId) });
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
renderChannel = ({
|
||||
onPress, testID, title, icon, checked, alert
|
||||
}) => {
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<Touch
|
||||
onPress={onPress}
|
||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
testID={testID}
|
||||
theme={theme}
|
||||
>
|
||||
<View style={[styles.button, { borderColor: themes[theme].separatorColor, marginVertical: 4 }]}>
|
||||
<CustomIcon style={[styles.buttonIcon, { color: themes[theme].controlText }]} size={24} name={icon} />
|
||||
<View style={styles.textContainer}>
|
||||
<Text style={[styles.buttonText, { color: themes[theme].bodyText }]}>{title}</Text>
|
||||
{ alert
|
||||
? (
|
||||
<Pressable onPress={() => this.showAlert()}>
|
||||
<CustomIcon style={[styles.buttonIcon, { color: themes[theme].dangerColor, transform: [{ rotateY: '180deg' }] }]} size={24} name={alert} />
|
||||
</Pressable>
|
||||
) : null}
|
||||
</View>
|
||||
{checked ? <CustomIcon name={checked} size={22} style={[styles.icon, { color: themes[theme].actionTintColor }]} /> : null}
|
||||
</View>
|
||||
</Touch>
|
||||
);
|
||||
}
|
||||
|
||||
isChecked = (rid) => {
|
||||
const { selected } = this.state;
|
||||
return selected.includes(rid);
|
||||
}
|
||||
|
||||
toggleChannel = (rid, roles) => {
|
||||
const { selected } = this.state;
|
||||
|
||||
if (roles) {
|
||||
this.showAlert();
|
||||
return;
|
||||
}
|
||||
|
||||
animateNextTransition();
|
||||
if (!this.isChecked(rid)) {
|
||||
this.setState({ selected: [...selected, rid] }, () => this.setHeader());
|
||||
} else {
|
||||
const filterSelected = selected.filter(el => el !== rid);
|
||||
this.setState({ selected: filterSelected }, () => this.setHeader());
|
||||
}
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => (
|
||||
<>
|
||||
{this.renderChannel({
|
||||
onPress: () => this.toggleChannel(item.rid, item.roles),
|
||||
title: item.name,
|
||||
icon: item.t === 'p' ? 'channel-private' : 'channel-public',
|
||||
checked: this.isChecked(item.rid, item.roles) ? 'check' : null,
|
||||
testID: 'select-list-view-item',
|
||||
alert: item.roles ? 'info' : null
|
||||
})}
|
||||
</>
|
||||
)
|
||||
|
||||
renderList = () => {
|
||||
const { data } = this.state;
|
||||
const { theme } = this.props;
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
data={data}
|
||||
extraData={this.state}
|
||||
keyExtractor={item => item._id}
|
||||
renderItem={this.renderItem}
|
||||
ListHeaderComponent={this.renderHeader}
|
||||
ItemSeparatorComponent={List.Separator}
|
||||
contentContainerStyle={{ backgroundColor: themes[theme].backgroundColor }}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loading } = this.state;
|
||||
|
||||
return (
|
||||
<SafeAreaView testID='new-message-view'>
|
||||
<StatusBar />
|
||||
{this.renderList()}
|
||||
<Loading visible={loading} />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isMasterDetail: state.app.isMasterDetail
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
leaveRoom: (rid, t) => dispatch(leaveRoomAction(rid, t))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SelectListView));
|
|
@ -50,7 +50,7 @@ class SelectedUsersView extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.init();
|
||||
|
||||
this.flatlist = React.createRef();
|
||||
const maxUsers = props.route.params?.maxUsers;
|
||||
this.state = {
|
||||
maxUsers,
|
||||
|
@ -190,9 +190,13 @@ class SelectedUsersView extends React.Component {
|
|||
if (users.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const ITEM_WIDTH = 250;
|
||||
return (
|
||||
<FlatList
|
||||
data={users}
|
||||
ref={ref => this.flatlist = ref}
|
||||
onContentSizeChange={() => this.flatlist.scrollToEnd()}
|
||||
getItemLayout={(_, index) => ({ length: ITEM_WIDTH, offset: ITEM_WIDTH * index, index })}
|
||||
keyExtractor={item => item._id}
|
||||
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
|
||||
contentContainerStyle={{ marginVertical: 5 }}
|
||||
|
|
|
@ -15,7 +15,7 @@ const Item = React.memo(({
|
|||
testID={testID}
|
||||
onPress={onPress}
|
||||
theme={theme}
|
||||
style={[styles.item, current && { backgroundColor: themes[theme].borderColor }]}
|
||||
style={({ pressed }) => [styles.item, current && { backgroundColor: pressed ? themes[theme].borderColor : themes[theme].bannerBackground }]}
|
||||
>
|
||||
<View style={styles.itemHorizontal}>
|
||||
{left}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Keyboard } from 'react-native';
|
||||
import { Keyboard, Alert } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
@ -28,6 +28,8 @@ import debounce from '../utils/debounce';
|
|||
import { showErrorAlert } from '../utils/info';
|
||||
import { goRoom } from '../utils/goRoom';
|
||||
import I18n from '../i18n';
|
||||
import { withActionSheet } from '../containers/ActionSheet';
|
||||
import { deleteRoom as deleteRoomAction } from '../actions/room';
|
||||
|
||||
const API_FETCH_COUNT = 25;
|
||||
|
||||
|
@ -47,12 +49,17 @@ class TeamChannelsView extends React.Component {
|
|||
theme: PropTypes.string,
|
||||
useRealName: PropTypes.bool,
|
||||
width: PropTypes.number,
|
||||
StoreLastMessage: PropTypes.bool
|
||||
StoreLastMessage: PropTypes.bool,
|
||||
addTeamChannelPermission: PropTypes.array,
|
||||
removeTeamChannelPermission: PropTypes.array,
|
||||
showActionSheet: PropTypes.func,
|
||||
deleteRoom: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.teamId = props.route.params?.teamId;
|
||||
this.rid = props.route.params?.rid;
|
||||
this.state = {
|
||||
loading: true,
|
||||
loadingMore: false,
|
||||
|
@ -60,9 +67,11 @@ class TeamChannelsView extends React.Component {
|
|||
isSearching: false,
|
||||
searchText: '',
|
||||
search: [],
|
||||
end: false
|
||||
end: false,
|
||||
showCreate: false
|
||||
};
|
||||
this.loadTeam();
|
||||
this.setHeader();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -70,6 +79,9 @@ class TeamChannelsView extends React.Component {
|
|||
}
|
||||
|
||||
loadTeam = async() => {
|
||||
const { addTeamChannelPermission } = this.props;
|
||||
const { loading } = this.state;
|
||||
|
||||
const db = database.active;
|
||||
try {
|
||||
const subCollection = db.get('subscriptions');
|
||||
|
@ -82,6 +94,14 @@ class TeamChannelsView extends React.Component {
|
|||
if (!this.team) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const permissions = await RocketChat.hasPermission([addTeamChannelPermission], this.team.rid);
|
||||
if (permissions[0]) {
|
||||
this.setState({ showCreate: true }, () => this.setHeader());
|
||||
}
|
||||
if (loading) {
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
} catch {
|
||||
const { navigation } = this.props;
|
||||
navigation.pop();
|
||||
|
@ -135,8 +155,8 @@ class TeamChannelsView extends React.Component {
|
|||
}
|
||||
}, 300)
|
||||
|
||||
getHeader = () => {
|
||||
const { isSearching } = this.state;
|
||||
setHeader = () => {
|
||||
const { isSearching, showCreate, data } = this.state;
|
||||
const {
|
||||
navigation, isMasterDetail, insets, theme
|
||||
} = this.props;
|
||||
|
@ -146,7 +166,7 @@ class TeamChannelsView extends React.Component {
|
|||
return;
|
||||
}
|
||||
|
||||
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 1 });
|
||||
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 2 });
|
||||
|
||||
if (isSearching) {
|
||||
return {
|
||||
|
@ -201,14 +221,11 @@ class TeamChannelsView extends React.Component {
|
|||
options.headerRight = () => (
|
||||
<HeaderButton.Container>
|
||||
<HeaderButton.Item iconName='search' onPress={this.onSearchPress} />
|
||||
{ showCreate
|
||||
? <HeaderButton.Item iconName='create' onPress={() => navigation.navigate('AddChannelTeamView', { teamId: this.teamId, teamChannels: data })} />
|
||||
: null}
|
||||
</HeaderButton.Container>
|
||||
);
|
||||
return options;
|
||||
}
|
||||
|
||||
setHeader = () => {
|
||||
const { navigation } = this.props;
|
||||
const options = this.getHeader();
|
||||
navigation.setOptions(options);
|
||||
}
|
||||
|
||||
|
@ -287,6 +304,93 @@ class TeamChannelsView extends React.Component {
|
|||
}
|
||||
}, 1000, true);
|
||||
|
||||
options = item => ([
|
||||
{
|
||||
title: I18n.t('Auto-join'),
|
||||
icon: item.t === 'p' ? 'channel-private' : 'channel-public'
|
||||
// onPress: this.autoJoin
|
||||
},
|
||||
{
|
||||
title: I18n.t('Remove_from_Team'),
|
||||
icon: 'close',
|
||||
danger: true,
|
||||
onPress: () => this.remove(item)
|
||||
},
|
||||
{
|
||||
title: I18n.t('Delete'),
|
||||
icon: 'delete',
|
||||
danger: true,
|
||||
onPress: () => this.delete(item)
|
||||
}
|
||||
])
|
||||
|
||||
remove = (item) => {
|
||||
Alert.alert(
|
||||
I18n.t('Confirmation'),
|
||||
I18n.t('Delete_Team_Room_Warning'),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('remove') }),
|
||||
style: 'destructive',
|
||||
onPress: () => this.removeRoom(item)
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
||||
}
|
||||
|
||||
removeRoom = async(item) => {
|
||||
try {
|
||||
const { data } = this.state;
|
||||
const result = await RocketChat.removeTeamRoom({ roomId: item.rid, teamId: this.team.teamId });
|
||||
if (result.success) {
|
||||
const newData = data.filter(room => result.room._id !== room.rid);
|
||||
this.setState({ loading: true, data: newData }, () => {
|
||||
this.load();
|
||||
this.loadTeam();
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
log(e);
|
||||
}
|
||||
}
|
||||
|
||||
delete = (item) => {
|
||||
const { deleteRoom } = this.props;
|
||||
|
||||
Alert.alert(
|
||||
I18n.t('Are_you_sure_question_mark'),
|
||||
I18n.t('Delete_Room_Warning'),
|
||||
[
|
||||
{
|
||||
text: I18n.t('Cancel'),
|
||||
style: 'cancel'
|
||||
},
|
||||
{
|
||||
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
|
||||
style: 'destructive',
|
||||
onPress: () => deleteRoom(item.rid, item.t)
|
||||
}
|
||||
],
|
||||
{ cancelable: false }
|
||||
);
|
||||
}
|
||||
|
||||
showChannelActions = async(item) => {
|
||||
logEvent(events.ROOM_SHOW_BOX_ACTIONS);
|
||||
const { showActionSheet, removeTeamChannelPermission } = this.props;
|
||||
|
||||
const permissions = await RocketChat.hasPermission([removeTeamChannelPermission], this.team.rid);
|
||||
if (!permissions[0]) {
|
||||
return;
|
||||
}
|
||||
showActionSheet({ options: this.options(item) });
|
||||
}
|
||||
|
||||
renderItem = ({ item }) => {
|
||||
const {
|
||||
StoreLastMessage,
|
||||
|
@ -302,6 +406,7 @@ class TeamChannelsView extends React.Component {
|
|||
showLastMessage={StoreLastMessage}
|
||||
onPress={this.onPressItem}
|
||||
width={width}
|
||||
onLongPress={this.showChannelActions}
|
||||
useRealName={useRealName}
|
||||
getRoomTitle={this.getRoomTitle}
|
||||
getRoomAvatar={this.getRoomAvatar}
|
||||
|
@ -365,7 +470,13 @@ const mapStateToProps = state => ({
|
|||
user: getUserSelector(state),
|
||||
useRealName: state.settings.UI_Use_Real_Name,
|
||||
isMasterDetail: state.app.isMasterDetail,
|
||||
StoreLastMessage: state.settings.Store_Last_Message
|
||||
StoreLastMessage: state.settings.Store_Last_Message,
|
||||
addTeamChannelPermission: state.permissions['add-team-channel'],
|
||||
removeTeamChannelPermission: state.permissions['remove-team-channel']
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(withDimensions(withSafeAreaInsets(withTheme(TeamChannelsView))));
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
deleteRoom: (rid, t) => dispatch(deleteRoomAction(rid, t))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withSafeAreaInsets(withTheme(withActionSheet(TeamChannelsView)))));
|
||||
|
|
|
@ -26,7 +26,7 @@ const styles = StyleSheet.create({
|
|||
const DropdownItem = React.memo(({
|
||||
theme, onPress, iconName, text
|
||||
}) => (
|
||||
<Touch theme={theme} onPress={onPress} style={{ backgroundColor: themes[theme].backgroundColor }}>
|
||||
<Touch theme={theme} onPress={onPress} style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }]}>
|
||||
<View style={styles.container}>
|
||||
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{text}</Text>
|
||||
{iconName ? <CustomIcon name={iconName} size={22} color={themes[theme].auxiliaryText} /> : null}
|
||||
|
|
|
@ -42,6 +42,11 @@ const data = {
|
|||
name: `detox-private-${ value }`
|
||||
}
|
||||
},
|
||||
teams: {
|
||||
private: {
|
||||
name: `detox-team-${ value }`
|
||||
}
|
||||
},
|
||||
registeringUser: {
|
||||
username: `newuser${ value }`,
|
||||
password: `password${ value }`,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue