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
|
<Touch
|
||||||
onPress={() => onPress(props.title)}
|
onPress={() => onPress(props.title)}
|
||||||
style={{ backgroundColor: backgroundColor || themes[props.theme].backgroundColor }}
|
style={({ pressed }) => [{ backgroundColor: pressed ? underlayColor || themes[props.theme].bannerBackground : backgroundColor || themes[props.theme].backgroundColor }]}
|
||||||
underlayColor={underlayColor}
|
|
||||||
enabled={!props.disabled}
|
enabled={!props.disabled}
|
||||||
theme={props.theme}
|
theme={props.theme}
|
||||||
>
|
>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { connect } from 'react-redux';
|
||||||
import { Base64 } from 'js-base64';
|
import { Base64 } from 'js-base64';
|
||||||
import * as AppleAuthentication from 'expo-apple-authentication';
|
import * as AppleAuthentication from 'expo-apple-authentication';
|
||||||
|
|
||||||
|
import { transparentize } from 'color2k';
|
||||||
import { withTheme } from '../theme';
|
import { withTheme } from '../theme';
|
||||||
import sharedStyles from '../views/Styles';
|
import sharedStyles from '../views/Styles';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
|
@ -344,10 +345,8 @@ class LoginServices extends React.PureComponent {
|
||||||
<Touch
|
<Touch
|
||||||
key={service.name}
|
key={service.name}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={[styles.serviceButton, { backgroundColor }]}
|
style={({ pressed }) => [styles.serviceButton, { backgroundColor: pressed ? transparentize(themes[theme].buttonText, 0.5) : backgroundColor }]}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
activeOpacity={0.5}
|
|
||||||
underlayColor={themes[theme].buttonText}
|
|
||||||
>
|
>
|
||||||
<View style={styles.serviceButtonContainer}>
|
<View style={styles.serviceButtonContainer}>
|
||||||
{service.authType === 'oauth' || service.authType === 'apple' ? <CustomIcon name={icon} size={24} color={themes[theme].titleText} style={styles.serviceIcon} /> : null}
|
{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 (
|
return (
|
||||||
<Touch
|
<Touch
|
||||||
style={[styles.buttonView, { backgroundColor: 'transparent' }]}
|
style={({ pressed }) => [styles.buttonView, { backgroundColor: pressed ? 'transparent' : themes[theme].bannerBackground }]}
|
||||||
underlayColor={themes[theme].passcodeButtonActive}
|
underlayColor={themes[theme].passcodeButtonActive}
|
||||||
rippleColor={themes[theme].passcodeButtonActive}
|
rippleColor={themes[theme].passcodeButtonActive}
|
||||||
enabled={!disabled}
|
enabled={!disabled}
|
||||||
|
|
|
@ -290,6 +290,7 @@
|
||||||
"last_message": "last message",
|
"last_message": "last message",
|
||||||
"Leave_channel": "Leave channel",
|
"Leave_channel": "Leave channel",
|
||||||
"leaving_room": "leaving room",
|
"leaving_room": "leaving room",
|
||||||
|
"Leave": "Leave",
|
||||||
"leave": "leave",
|
"leave": "leave",
|
||||||
"Legal": "Legal",
|
"Legal": "Legal",
|
||||||
"Light": "Light",
|
"Light": "Light",
|
||||||
|
@ -435,6 +436,7 @@
|
||||||
"Review_app_unable_store": "Unable to open {{store}}",
|
"Review_app_unable_store": "Unable to open {{store}}",
|
||||||
"Review_this_app": "Review this app",
|
"Review_this_app": "Review this app",
|
||||||
"Remove": "Remove",
|
"Remove": "Remove",
|
||||||
|
"remove": "remove",
|
||||||
"Roles": "Roles",
|
"Roles": "Roles",
|
||||||
"Room_actions": "Room actions",
|
"Room_actions": "Room actions",
|
||||||
"Room_changed_announcement": "Room announcement changed to: {{announcement}} by {{userBy}}",
|
"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}}",
|
"This_room_encryption_has_been_disabled_by__username_": "This room's encryption has been disabled by {{username}}",
|
||||||
"Teams": "Teams",
|
"Teams": "Teams",
|
||||||
"No_team_channels_found": "No channels found",
|
"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-c-room',
|
||||||
'add-user-to-any-p-room',
|
'add-user-to-any-p-room',
|
||||||
'add-user-to-joined-room',
|
'add-user-to-joined-room',
|
||||||
|
'add-team-member',
|
||||||
|
'add-team-channel',
|
||||||
'archive-room',
|
'archive-room',
|
||||||
'auto-translate',
|
'auto-translate',
|
||||||
'create-invite-links',
|
'create-invite-links',
|
||||||
|
@ -21,11 +23,14 @@ const PERMISSIONS = [
|
||||||
'delete-p',
|
'delete-p',
|
||||||
'edit-message',
|
'edit-message',
|
||||||
'edit-room',
|
'edit-room',
|
||||||
|
'edit-team-member',
|
||||||
|
'edit-team-channel',
|
||||||
'force-delete-message',
|
'force-delete-message',
|
||||||
'mute-user',
|
'mute-user',
|
||||||
'pin-message',
|
'pin-message',
|
||||||
'post-readonly',
|
'post-readonly',
|
||||||
'remove-user',
|
'remove-user',
|
||||||
|
'remove-team-channel',
|
||||||
'set-leader',
|
'set-leader',
|
||||||
'set-moderator',
|
'set-moderator',
|
||||||
'set-owner',
|
'set-owner',
|
||||||
|
@ -38,7 +43,9 @@ const PERMISSIONS = [
|
||||||
'view-privileged-setting',
|
'view-privileged-setting',
|
||||||
'view-room-administration',
|
'view-room-administration',
|
||||||
'view-statistics',
|
'view-statistics',
|
||||||
'view-user-administration'
|
'view-user-administration',
|
||||||
|
'view-all-teams',
|
||||||
|
'view-all-team-channels'
|
||||||
];
|
];
|
||||||
|
|
||||||
export async function setPermissions() {
|
export async function setPermissions() {
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
import log from '../../utils/log';
|
import log from '../../utils/log';
|
||||||
import updateMessages from './updateMessages';
|
import updateMessages from './updateMessages';
|
||||||
|
|
||||||
async function load({ rid: roomId, latest, t }) {
|
async function load({
|
||||||
let params = { roomId, count: 50 };
|
rid: roomId, latest, t, team
|
||||||
|
}) {
|
||||||
|
let params = { roomId: roomId || team.roomId, count: 50 };
|
||||||
|
let apiType;
|
||||||
if (latest) {
|
if (latest) {
|
||||||
params = { ...params, latest: new Date(latest).toISOString() };
|
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) {
|
if (!apiType) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -728,7 +728,54 @@ const RocketChat = {
|
||||||
prid, pmid, t_name, reply, users, encrypted
|
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) {
|
joinRoom(roomId, joinCode, type) {
|
||||||
// TODO: join code
|
// TODO: join code
|
||||||
// RC 0.48.0
|
// RC 0.48.0
|
||||||
|
|
|
@ -24,7 +24,6 @@ const RoomItem = ({
|
||||||
status,
|
status,
|
||||||
useRealName,
|
useRealName,
|
||||||
theme,
|
theme,
|
||||||
isFocused,
|
|
||||||
isGroupChat,
|
isGroupChat,
|
||||||
isRead,
|
isRead,
|
||||||
date,
|
date,
|
||||||
|
@ -42,6 +41,7 @@ const RoomItem = ({
|
||||||
testID,
|
testID,
|
||||||
swipeEnabled,
|
swipeEnabled,
|
||||||
onPress,
|
onPress,
|
||||||
|
onLongPress,
|
||||||
toggleFav,
|
toggleFav,
|
||||||
toggleRead,
|
toggleRead,
|
||||||
hideChannel,
|
hideChannel,
|
||||||
|
@ -49,6 +49,7 @@ const RoomItem = ({
|
||||||
}) => (
|
}) => (
|
||||||
<Touchable
|
<Touchable
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
|
onLongPress={onLongPress}
|
||||||
width={width}
|
width={width}
|
||||||
favorite={favorite}
|
favorite={favorite}
|
||||||
toggleFav={toggleFav}
|
toggleFav={toggleFav}
|
||||||
|
@ -59,7 +60,6 @@ const RoomItem = ({
|
||||||
testID={testID}
|
testID={testID}
|
||||||
type={type}
|
type={type}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
isFocused={isFocused}
|
|
||||||
swipeEnabled={swipeEnabled}
|
swipeEnabled={swipeEnabled}
|
||||||
>
|
>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
|
@ -161,7 +161,6 @@ RoomItem.propTypes = {
|
||||||
status: PropTypes.string,
|
status: PropTypes.string,
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
isFocused: PropTypes.bool,
|
|
||||||
isGroupChat: PropTypes.bool,
|
isGroupChat: PropTypes.bool,
|
||||||
isRead: PropTypes.bool,
|
isRead: PropTypes.bool,
|
||||||
teamMain: PropTypes.bool,
|
teamMain: PropTypes.bool,
|
||||||
|
@ -181,6 +180,7 @@ RoomItem.propTypes = {
|
||||||
toggleFav: PropTypes.func,
|
toggleFav: PropTypes.func,
|
||||||
toggleRead: PropTypes.func,
|
toggleRead: PropTypes.func,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
|
onLongPress: PropTypes.func,
|
||||||
hideChannel: PropTypes.func
|
hideChannel: PropTypes.func
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Touchable extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
type: PropTypes.string.isRequired,
|
type: PropTypes.string.isRequired,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
|
onLongPress: PropTypes.func,
|
||||||
testID: PropTypes.string,
|
testID: PropTypes.string,
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
favorite: PropTypes.bool,
|
favorite: PropTypes.bool,
|
||||||
|
@ -27,7 +28,6 @@ class Touchable extends React.Component {
|
||||||
hideChannel: PropTypes.func,
|
hideChannel: PropTypes.func,
|
||||||
children: PropTypes.element,
|
children: PropTypes.element,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
isFocused: PropTypes.bool,
|
|
||||||
swipeEnabled: 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() {
|
render() {
|
||||||
const {
|
const {
|
||||||
testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled
|
testID, isRead, width, favorite, children, theme, swipeEnabled
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -239,11 +251,10 @@ class Touchable extends React.Component {
|
||||||
>
|
>
|
||||||
<Touch
|
<Touch
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
|
onLongPress={this.onLongPress}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
style={{
|
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].chatComponentBackground : themes[theme].backgroundColor }]}
|
||||||
backgroundColor: isFocused ? themes[theme].chatComponentBackground : themes[theme].backgroundColor
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Touch>
|
</Touch>
|
||||||
|
|
|
@ -14,7 +14,6 @@ const attrs = [
|
||||||
'status',
|
'status',
|
||||||
'connected',
|
'connected',
|
||||||
'theme',
|
'theme',
|
||||||
'isFocused',
|
|
||||||
'forceUpdate',
|
'forceUpdate',
|
||||||
'showLastMessage'
|
'showLastMessage'
|
||||||
];
|
];
|
||||||
|
@ -25,6 +24,7 @@ class RoomItemContainer extends React.Component {
|
||||||
showLastMessage: PropTypes.bool,
|
showLastMessage: PropTypes.bool,
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
|
onLongPress: PropTypes.func,
|
||||||
username: PropTypes.string,
|
username: PropTypes.string,
|
||||||
avatarSize: PropTypes.number,
|
avatarSize: PropTypes.number,
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
|
@ -36,7 +36,6 @@ class RoomItemContainer extends React.Component {
|
||||||
getUserPresence: PropTypes.func,
|
getUserPresence: PropTypes.func,
|
||||||
connected: PropTypes.bool,
|
connected: PropTypes.bool,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
isFocused: PropTypes.bool,
|
|
||||||
getRoomTitle: PropTypes.func,
|
getRoomTitle: PropTypes.func,
|
||||||
getRoomAvatar: PropTypes.func,
|
getRoomAvatar: PropTypes.func,
|
||||||
getIsGroupChat: PropTypes.func,
|
getIsGroupChat: PropTypes.func,
|
||||||
|
@ -112,6 +111,11 @@ class RoomItemContainer extends React.Component {
|
||||||
return onPress(item);
|
return onPress(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onLongPress = () => {
|
||||||
|
const { item, onLongPress } = this.props;
|
||||||
|
return onLongPress(item);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
item,
|
item,
|
||||||
|
@ -123,7 +127,6 @@ class RoomItemContainer extends React.Component {
|
||||||
toggleRead,
|
toggleRead,
|
||||||
hideChannel,
|
hideChannel,
|
||||||
theme,
|
theme,
|
||||||
isFocused,
|
|
||||||
avatarSize,
|
avatarSize,
|
||||||
status,
|
status,
|
||||||
showLastMessage,
|
showLastMessage,
|
||||||
|
@ -160,6 +163,7 @@ class RoomItemContainer extends React.Component {
|
||||||
isGroupChat={this.isGroupChat}
|
isGroupChat={this.isGroupChat}
|
||||||
isRead={isRead}
|
isRead={isRead}
|
||||||
onPress={this.onPress}
|
onPress={this.onPress}
|
||||||
|
onLongPress={this.onLongPress}
|
||||||
date={date}
|
date={date}
|
||||||
accessibilityLabel={accessibilityLabel}
|
accessibilityLabel={accessibilityLabel}
|
||||||
width={width}
|
width={width}
|
||||||
|
@ -171,7 +175,6 @@ class RoomItemContainer extends React.Component {
|
||||||
testID={testID}
|
testID={testID}
|
||||||
type={item.t}
|
type={item.t}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
isFocused={isFocused}
|
|
||||||
size={avatarSize}
|
size={avatarSize}
|
||||||
prid={item.prid}
|
prid={item.prid}
|
||||||
status={status}
|
status={status}
|
||||||
|
|
|
@ -21,6 +21,14 @@ const createGroupChat = function createGroupChat() {
|
||||||
return RocketChat.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 }) {
|
const handleRequest = function* handleRequest({ data }) {
|
||||||
try {
|
try {
|
||||||
const auth = yield select(state => state.login.isAuthenticated);
|
const auth = yield select(state => state.login.isAuthenticated);
|
||||||
|
@ -29,7 +37,21 @@ const handleRequest = function* handleRequest({ data }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
let sub;
|
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);
|
logEvent(events.SELECTED_USERS_CREATE_GROUP);
|
||||||
const result = yield call(createGroupChat);
|
const result = yield call(createGroupChat);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
|
@ -49,14 +71,22 @@ const handleRequest = function* handleRequest({ data }) {
|
||||||
encrypted
|
encrypted
|
||||||
});
|
});
|
||||||
sub = yield call(createChannel, data);
|
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 {
|
try {
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
const subCollection = db.get('subscriptions');
|
const subCollection = db.get('subscriptions');
|
||||||
yield db.action(async() => {
|
yield db.action(async() => {
|
||||||
await subCollection.create((s) => {
|
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);
|
Object.assign(s, sub);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -76,12 +106,12 @@ const handleSuccess = function* handleSuccess({ data }) {
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
Navigation.navigate('DrawerNavigator');
|
Navigation.navigate('DrawerNavigator');
|
||||||
}
|
}
|
||||||
goRoom({ item: data, isMasterDetail });
|
goRoom({ item: data.team ? data.team : data, isMasterDetail });
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFailure = function handleFailure({ err }) {
|
const handleFailure = function handleFailure({ err }) {
|
||||||
setTimeout(() => {
|
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);
|
showErrorAlert(msg);
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
|
@ -71,6 +71,9 @@ import ShareView from '../views/ShareView';
|
||||||
import CreateDiscussionView from '../views/CreateDiscussionView';
|
import CreateDiscussionView from '../views/CreateDiscussionView';
|
||||||
|
|
||||||
import QueueListView from '../ee/omnichannel/views/QueueListView';
|
import QueueListView from '../ee/omnichannel/views/QueueListView';
|
||||||
|
import AddChannelTeamView from '../views/AddChannelTeamView';
|
||||||
|
import AddExistingChannelView from '../views/AddExistingChannelView';
|
||||||
|
import SelectListView from '../views/SelectListView';
|
||||||
|
|
||||||
// ChatsStackNavigator
|
// ChatsStackNavigator
|
||||||
const ChatsStack = createStackNavigator();
|
const ChatsStack = createStackNavigator();
|
||||||
|
@ -91,6 +94,11 @@ const ChatsStackNavigator = () => {
|
||||||
component={RoomActionsView}
|
component={RoomActionsView}
|
||||||
options={RoomActionsView.navigationOptions}
|
options={RoomActionsView.navigationOptions}
|
||||||
/>
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='SelectListView'
|
||||||
|
component={SelectListView}
|
||||||
|
options={SelectListView.navigationOptions}
|
||||||
|
/>
|
||||||
<ChatsStack.Screen
|
<ChatsStack.Screen
|
||||||
name='RoomInfoView'
|
name='RoomInfoView'
|
||||||
component={RoomInfoView}
|
component={RoomInfoView}
|
||||||
|
@ -174,6 +182,16 @@ const ChatsStackNavigator = () => {
|
||||||
component={TeamChannelsView}
|
component={TeamChannelsView}
|
||||||
options={TeamChannelsView.navigationOptions}
|
options={TeamChannelsView.navigationOptions}
|
||||||
/>
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='AddChannelTeamView'
|
||||||
|
component={AddChannelTeamView}
|
||||||
|
options={AddChannelTeamView.navigationOptions}
|
||||||
|
/>
|
||||||
|
<ChatsStack.Screen
|
||||||
|
name='AddExistingChannelView'
|
||||||
|
component={AddExistingChannelView}
|
||||||
|
options={AddExistingChannelView.navigationOptions}
|
||||||
|
/>
|
||||||
<ChatsStack.Screen
|
<ChatsStack.Screen
|
||||||
name='MarkdownTableView'
|
name='MarkdownTableView'
|
||||||
component={MarkdownTableView}
|
component={MarkdownTableView}
|
||||||
|
|
|
@ -8,10 +8,28 @@ const navigate = ({ item, isMasterDetail, ...props }) => {
|
||||||
navigationMethod = Navigation.replace;
|
navigationMethod = Navigation.replace;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationMethod('RoomView', {
|
if (item.isTeamChannel) {
|
||||||
rid: item.rid,
|
// TODO: Refactor
|
||||||
|
Navigation.navigate('TeamChannelsView');
|
||||||
|
Navigation.push('RoomView', {
|
||||||
|
rid: item.roomId || item.rid,
|
||||||
name: RocketChat.getRoomTitle(item),
|
name: RocketChat.getRoomTitle(item),
|
||||||
t: item.t,
|
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,
|
prid: item.prid,
|
||||||
room: item,
|
room: item,
|
||||||
search: item.search,
|
search: item.search,
|
||||||
|
@ -19,6 +37,7 @@ const navigate = ({ item, isMasterDetail, ...props }) => {
|
||||||
roomUserId: RocketChat.getUidDirectMessage(item),
|
roomUserId: RocketChat.getUidDirectMessage(item),
|
||||||
...props
|
...props
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const goRoom = async({ item = {}, isMasterDetail = false, ...props }) => {
|
export const goRoom = async({ item = {}, isMasterDetail = false, ...props }) => {
|
||||||
|
|
|
@ -88,6 +88,7 @@ export default {
|
||||||
|
|
||||||
// NEW MESSAGE VIEW
|
// NEW MESSAGE VIEW
|
||||||
NEW_MSG_CREATE_CHANNEL: 'new_msg_create_channel',
|
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_GROUP_CHAT: 'new_msg_create_group_chat',
|
||||||
NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion',
|
NEW_MSG_CREATE_DISCUSSION: 'new_msg_create_discussion',
|
||||||
NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user',
|
NEW_MSG_CHAT_WITH_USER: 'new_msg_chat_with_user',
|
||||||
|
@ -100,12 +101,16 @@ export default {
|
||||||
|
|
||||||
// CREATE CHANNEL VIEW
|
// CREATE CHANNEL VIEW
|
||||||
CR_CREATE: 'cr_create',
|
CR_CREATE: 'cr_create',
|
||||||
|
CT_CREATE: 'ct_create',
|
||||||
CR_CREATE_F: 'cr_create_f',
|
CR_CREATE_F: 'cr_create_f',
|
||||||
|
CT_CREATE_F: 'ct_create_f',
|
||||||
CR_TOGGLE_TYPE: 'cr_toggle_type',
|
CR_TOGGLE_TYPE: 'cr_toggle_type',
|
||||||
CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
|
CR_TOGGLE_READ_ONLY: 'cr_toggle_read_only',
|
||||||
CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
|
CR_TOGGLE_BROADCAST: 'cr_toggle_broadcast',
|
||||||
CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
|
CR_TOGGLE_ENCRYPTED: 'cr_toggle_encrypted',
|
||||||
CR_REMOVE_USER: 'cr_remove_user',
|
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
|
// CREATE DISCUSSION VIEW
|
||||||
CD_CREATE: 'cd_create',
|
CD_CREATE: 'cd_create',
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { RectButton } from 'react-native-gesture-handler';
|
|
||||||
|
|
||||||
|
import { Pressable } from 'react-native';
|
||||||
import { themes } from '../constants/colors';
|
import { themes } from '../constants/colors';
|
||||||
|
|
||||||
class Touch extends React.Component {
|
class Touch extends React.Component {
|
||||||
|
@ -15,20 +15,21 @@ class Touch extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
children, onPress, theme, underlayColor, ...props
|
children, onPress, onLongPress, theme, style, ...props
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RectButton
|
<Pressable
|
||||||
ref={this.getRef}
|
ref={this.getRef}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
|
onLongPress={onLongPress}
|
||||||
activeOpacity={1}
|
activeOpacity={1}
|
||||||
underlayColor={underlayColor || themes[theme].bannerBackground}
|
style={style}
|
||||||
rippleColor={themes[theme].bannerBackground}
|
android_ripple={{ color: themes[theme].bannerBackground }}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</RectButton>
|
</Pressable>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,8 +37,10 @@ class Touch extends React.Component {
|
||||||
Touch.propTypes = {
|
Touch.propTypes = {
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
onPress: PropTypes.func,
|
onPress: PropTypes.func,
|
||||||
|
onLongPress: PropTypes.func,
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
underlayColor: PropTypes.string
|
underlayColor: PropTypes.string,
|
||||||
|
style: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Touch;
|
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 {
|
class CreateChannelView extends React.Component {
|
||||||
static navigationOptions = () => ({
|
static navigationOptions = () => ({
|
||||||
title: I18n.t('Create_Channel')
|
title: this.isTeam ? I18n.t('Create_Team') : I18n.t('Create_Channel')
|
||||||
});
|
})
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
|
route: PropTypes.object,
|
||||||
baseUrl: PropTypes.string,
|
baseUrl: PropTypes.string,
|
||||||
create: PropTypes.func.isRequired,
|
create: PropTypes.func.isRequired,
|
||||||
removeUser: PropTypes.func.isRequired,
|
removeUser: PropTypes.func.isRequired,
|
||||||
|
@ -86,15 +87,22 @@ class CreateChannelView extends React.Component {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
token: PropTypes.string
|
token: PropTypes.string
|
||||||
}),
|
}),
|
||||||
theme: PropTypes.string
|
theme: PropTypes.string,
|
||||||
|
teamId: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const { route } = this.props;
|
||||||
|
this.isTeam = route?.params?.isTeam || false;
|
||||||
|
this.teamId = route?.params?.teamId;
|
||||||
|
this.state = {
|
||||||
channelName: '',
|
channelName: '',
|
||||||
type: true,
|
type: true,
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
encrypted: false,
|
encrypted: false,
|
||||||
broadcast: false
|
broadcast: false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldComponentUpdate(nextProps, nextState) {
|
shouldComponentUpdate(nextProps, nextState) {
|
||||||
|
@ -154,7 +162,9 @@ class CreateChannelView extends React.Component {
|
||||||
const {
|
const {
|
||||||
channelName, type, readOnly, broadcast, encrypted
|
channelName, type, readOnly, broadcast, encrypted
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const { users: usersProps, isFetching, create } = this.props;
|
const {
|
||||||
|
users: usersProps, isFetching, create
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
if (!channelName.trim() || isFetching) {
|
if (!channelName.trim() || isFetching) {
|
||||||
return;
|
return;
|
||||||
|
@ -163,9 +173,9 @@ class CreateChannelView extends React.Component {
|
||||||
// transform users object into array of usernames
|
// transform users object into array of usernames
|
||||||
const users = usersProps.map(user => user.name);
|
const users = usersProps.map(user => user.name);
|
||||||
|
|
||||||
// create channel
|
// create channel or team
|
||||||
create({
|
create({
|
||||||
name: channelName, users, type, readOnly, broadcast, encrypted
|
name: channelName, users, type, readOnly, broadcast, encrypted, isTeam: this.isTeam, teamId: this.teamId
|
||||||
});
|
});
|
||||||
|
|
||||||
Review.pushPositiveEvent();
|
Review.pushPositiveEvent();
|
||||||
|
@ -197,10 +207,11 @@ class CreateChannelView extends React.Component {
|
||||||
|
|
||||||
renderType() {
|
renderType() {
|
||||||
const { type } = this.state;
|
const { type } = this.state;
|
||||||
|
|
||||||
return this.renderSwitch({
|
return this.renderSwitch({
|
||||||
id: 'type',
|
id: 'type',
|
||||||
value: type,
|
value: type,
|
||||||
label: 'Private_Channel',
|
label: this.isTeam ? 'Private_Team' : 'Private_Channel',
|
||||||
onValueChange: (value) => {
|
onValueChange: (value) => {
|
||||||
logEvent(events.CR_TOGGLE_TYPE);
|
logEvent(events.CR_TOGGLE_TYPE);
|
||||||
// If we set the channel as public, encrypted status should be false
|
// If we set the channel as public, encrypted status should be false
|
||||||
|
@ -211,10 +222,11 @@ class CreateChannelView extends React.Component {
|
||||||
|
|
||||||
renderReadOnly() {
|
renderReadOnly() {
|
||||||
const { readOnly, broadcast } = this.state;
|
const { readOnly, broadcast } = this.state;
|
||||||
|
|
||||||
return this.renderSwitch({
|
return this.renderSwitch({
|
||||||
id: 'readonly',
|
id: 'readonly',
|
||||||
value: readOnly,
|
value: readOnly,
|
||||||
label: 'Read_Only_Channel',
|
label: this.isTeam ? 'Read_Only_Team' : 'Read_Only_Channel',
|
||||||
onValueChange: (value) => {
|
onValueChange: (value) => {
|
||||||
logEvent(events.CR_TOGGLE_READ_ONLY);
|
logEvent(events.CR_TOGGLE_READ_ONLY);
|
||||||
this.setState({ readOnly: value });
|
this.setState({ readOnly: value });
|
||||||
|
@ -245,10 +257,11 @@ class CreateChannelView extends React.Component {
|
||||||
|
|
||||||
renderBroadcast() {
|
renderBroadcast() {
|
||||||
const { broadcast, readOnly } = this.state;
|
const { broadcast, readOnly } = this.state;
|
||||||
|
|
||||||
return this.renderSwitch({
|
return this.renderSwitch({
|
||||||
id: 'broadcast',
|
id: 'broadcast',
|
||||||
value: broadcast,
|
value: broadcast,
|
||||||
label: 'Broadcast_Channel',
|
label: this.isTeam ? 'Broadcast_Team' : 'Broadcast_Channel',
|
||||||
onValueChange: (value) => {
|
onValueChange: (value) => {
|
||||||
logEvent(events.CR_TOGGLE_BROADCAST);
|
logEvent(events.CR_TOGGLE_BROADCAST);
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -302,7 +315,9 @@ class CreateChannelView extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { channelName } = this.state;
|
const { channelName } = this.state;
|
||||||
const { users, isFetching, theme } = this.props;
|
const {
|
||||||
|
users, isFetching, theme
|
||||||
|
} = this.props;
|
||||||
const userCount = users.length;
|
const userCount = users.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -312,18 +327,18 @@ class CreateChannelView extends React.Component {
|
||||||
keyboardVerticalOffset={128}
|
keyboardVerticalOffset={128}
|
||||||
>
|
>
|
||||||
<StatusBar />
|
<StatusBar />
|
||||||
<SafeAreaView testID='create-channel-view'>
|
<SafeAreaView testID={this.isTeam ? 'create-team-view' : 'create-channel-view'}>
|
||||||
<ScrollView {...scrollPersistTaps}>
|
<ScrollView {...scrollPersistTaps}>
|
||||||
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
|
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
|
||||||
<TextInput
|
<TextInput
|
||||||
autoFocus
|
autoFocus
|
||||||
style={[styles.input, { backgroundColor: themes[theme].backgroundColor }]}
|
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}
|
value={channelName}
|
||||||
onChangeText={this.onChangeText}
|
onChangeText={this.onChangeText}
|
||||||
placeholder={I18n.t('Channel_Name')}
|
placeholder={this.isTeam ? I18n.t('Team_Name') : I18n.t('Channel_Name')}
|
||||||
returnKeyType='done'
|
returnKeyType='done'
|
||||||
testID='create-channel-name'
|
testID={this.isTeam ? 'create-team-name' : 'create-channel-name'}
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
autoCapitalize='none'
|
autoCapitalize='none'
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
@ -67,7 +67,7 @@ export default class DirectoryOptions extends PureComponent {
|
||||||
return (
|
return (
|
||||||
<Touch
|
<Touch
|
||||||
onPress={() => changeType(itemType)}
|
onPress={() => changeType(itemType)}
|
||||||
style={styles.dropdownItemButton}
|
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }, styles.dropdownItemButton]}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
>
|
>
|
||||||
<View style={styles.dropdownItemContainer}>
|
<View style={styles.dropdownItemContainer}>
|
||||||
|
|
|
@ -169,7 +169,7 @@ class DirectoryView extends React.Component {
|
||||||
/>
|
/>
|
||||||
<Touch
|
<Touch
|
||||||
onPress={this.toggleDropdown}
|
onPress={this.toggleDropdown}
|
||||||
style={styles.dropdownItemButton}
|
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }, styles.dropdownItemButton]}
|
||||||
testID='directory-view-dropdown'
|
testID='directory-view-dropdown'
|
||||||
theme={theme}
|
theme={theme}
|
||||||
>
|
>
|
||||||
|
|
|
@ -116,6 +116,12 @@ class NewMessageView extends React.Component {
|
||||||
navigation.navigate('SelectedUsersViewCreateChannel', { nextAction: () => navigation.navigate('CreateChannelView') });
|
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 = () => {
|
createGroupChat = () => {
|
||||||
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
|
logEvent(events.NEW_MSG_CREATE_GROUP_CHAT);
|
||||||
const { createChannel, maxUsers, navigation } = this.props;
|
const { createChannel, maxUsers, navigation } = this.props;
|
||||||
|
@ -142,7 +148,9 @@ class NewMessageView extends React.Component {
|
||||||
return (
|
return (
|
||||||
<Touch
|
<Touch
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
style={({ pressed }) => [{
|
||||||
|
backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor
|
||||||
|
}]}
|
||||||
testID={testID}
|
testID={testID}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
>
|
>
|
||||||
|
@ -172,6 +180,12 @@ class NewMessageView extends React.Component {
|
||||||
testID: 'new-message-view-create-channel',
|
testID: 'new-message-view-create-channel',
|
||||||
first: true
|
first: true
|
||||||
})}
|
})}
|
||||||
|
{this.renderButton({
|
||||||
|
onPress: this.createTeam,
|
||||||
|
title: I18n.t('Create_Team'),
|
||||||
|
icon: 'team',
|
||||||
|
testID: 'new-message-view-create-team'
|
||||||
|
})}
|
||||||
{maxUsers > 2 ? this.renderButton({
|
{maxUsers > 2 ? this.renderButton({
|
||||||
onPress: this.createGroupChat,
|
onPress: this.createGroupChat,
|
||||||
title: I18n.t('Create_Direct_Messages'),
|
title: I18n.t('Create_Direct_Messages'),
|
||||||
|
@ -253,7 +267,7 @@ const mapStateToProps = state => ({
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
createChannel: params => dispatch(createChannelRequest(params))
|
create: params => dispatch(createChannelRequest(params))
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
|
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(NewMessageView));
|
||||||
|
|
|
@ -309,7 +309,7 @@ class ProfileView extends React.Component {
|
||||||
key={key}
|
key={key}
|
||||||
testID={key}
|
testID={key}
|
||||||
onPress={onPress}
|
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}
|
enabled={!disabled}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
>
|
>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { compareServerVersion, methods } from '../../lib/utils';
|
import { compareServerVersion, methods } from '../../lib/utils';
|
||||||
|
|
||||||
import Touch from '../../utils/touch';
|
import Touch from '../../utils/touch';
|
||||||
|
@ -60,7 +61,8 @@ class RoomActionsView extends React.Component {
|
||||||
editRoomPermission: PropTypes.array,
|
editRoomPermission: PropTypes.array,
|
||||||
toggleRoomE2EEncryptionPermission: PropTypes.array,
|
toggleRoomE2EEncryptionPermission: PropTypes.array,
|
||||||
viewBroadcastMemberListPermission: PropTypes.array,
|
viewBroadcastMemberListPermission: PropTypes.array,
|
||||||
transferLivechatGuestPermission: PropTypes.array
|
transferLivechatGuestPermission: PropTypes.array,
|
||||||
|
addTeamMemberPermission: PropTypes.array
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -170,12 +172,19 @@ class RoomActionsView extends React.Component {
|
||||||
|
|
||||||
canAddUser = async() => {
|
canAddUser = async() => {
|
||||||
const { room, joined } = this.state;
|
const { room, joined } = this.state;
|
||||||
const { addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission } = this.props;
|
const {
|
||||||
|
addUserToJoinedRoomPermission, addUserToAnyCRoomPermission, addUserToAnyPRoomPermission, addTeamMemberPermission
|
||||||
|
} = this.props;
|
||||||
const { rid, t } = room;
|
const { rid, t } = room;
|
||||||
let canAddUser = false;
|
let canAddUser = false;
|
||||||
|
let permissions;
|
||||||
|
|
||||||
const userInRoom = joined;
|
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]) {
|
if (userInRoom && permissions[0]) {
|
||||||
canAddUser = true;
|
canAddUser = true;
|
||||||
|
@ -320,6 +329,31 @@ class RoomActionsView extends React.Component {
|
||||||
setLoadingInvite(true);
|
setLoadingInvite(true);
|
||||||
await RocketChat.addUsersToRoom(rid);
|
await RocketChat.addUsersToRoom(rid);
|
||||||
navigation.pop();
|
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) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
} finally {
|
} 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 = () => {
|
renderRoomInfo = () => {
|
||||||
const { room, member } = this.state;
|
const { room, member } = this.state;
|
||||||
const {
|
const {
|
||||||
|
@ -433,7 +517,7 @@ class RoomActionsView extends React.Component {
|
||||||
rid, t, room, member
|
rid, t, room, member
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
style={{ backgroundColor: themes[theme].backgroundColor }}
|
style={({ pressed }) => [{ backgroundColor: pressed ? themes[theme].bannerBackground : themes[theme].backgroundColor }]}
|
||||||
accessibilityLabel={I18n.t('Room_Info')}
|
accessibilityLabel={I18n.t('Room_Info')}
|
||||||
accessibilityTraits='button'
|
accessibilityTraits='button'
|
||||||
enabled={!isGroupChat}
|
enabled={!isGroupChat}
|
||||||
|
@ -568,9 +652,9 @@ class RoomActionsView extends React.Component {
|
||||||
<List.Section>
|
<List.Section>
|
||||||
<List.Separator />
|
<List.Separator />
|
||||||
<List.Item
|
<List.Item
|
||||||
title='Leave_channel'
|
title={room.teamId && room.teamMain ? 'Leave' : 'Leave_channel'}
|
||||||
onPress={() => this.onPressTouchable({
|
onPress={() => this.onPressTouchable({
|
||||||
event: this.leaveChannel
|
event: room.teamId && room.teamMain ? this.leaveTeam : this.leaveChannel
|
||||||
})}
|
})}
|
||||||
testID='room-actions-leave-channel'
|
testID='room-actions-leave-channel'
|
||||||
left={() => <List.Icon name='logout' color={themes[theme].dangerColor} />}
|
left={() => <List.Icon name='logout' color={themes[theme].dangerColor} />}
|
||||||
|
@ -629,7 +713,7 @@ class RoomActionsView extends React.Component {
|
||||||
params: {
|
params: {
|
||||||
rid,
|
rid,
|
||||||
title: I18n.t('Add_users'),
|
title: I18n.t('Add_users'),
|
||||||
nextAction: this.addUser
|
nextAction: room.teamId ? this.addMemberToTeam : this.addUser
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
testID='room-actions-add-user'
|
testID='room-actions-add-user'
|
||||||
|
@ -881,6 +965,7 @@ const mapStateToProps = state => ({
|
||||||
encryptionEnabled: state.encryption.enabled,
|
encryptionEnabled: state.encryption.enabled,
|
||||||
serverVersion: state.server.version,
|
serverVersion: state.server.version,
|
||||||
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
addUserToJoinedRoomPermission: state.permissions['add-user-to-joined-room'],
|
||||||
|
addTeamMemberPermission: state.permissions['add-team-member'],
|
||||||
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
addUserToAnyCRoomPermission: state.permissions['add-user-to-any-c-room'],
|
||||||
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
addUserToAnyPRoomPermission: state.permissions['add-user-to-any-p-room'],
|
||||||
createInviteLinksPermission: state.permissions['create-invite-links'],
|
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 ImagePicker from 'react-native-image-crop-picker';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import isEmpty from 'lodash/isEmpty';
|
import isEmpty from 'lodash/isEmpty';
|
||||||
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { compareServerVersion, methods } from '../../lib/utils';
|
import { compareServerVersion, methods } from '../../lib/utils';
|
||||||
|
|
||||||
import database from '../../lib/database';
|
import database from '../../lib/database';
|
||||||
|
@ -41,6 +42,7 @@ const PERMISSION_ARCHIVE = 'archive-room';
|
||||||
const PERMISSION_UNARCHIVE = 'unarchive-room';
|
const PERMISSION_UNARCHIVE = 'unarchive-room';
|
||||||
const PERMISSION_DELETE_C = 'delete-c';
|
const PERMISSION_DELETE_C = 'delete-c';
|
||||||
const PERMISSION_DELETE_P = 'delete-p';
|
const PERMISSION_DELETE_P = 'delete-p';
|
||||||
|
const PERMISSION_EDIT_TEAM_CHANNEL = 'edit-team-channel';
|
||||||
|
|
||||||
class RoomInfoEditView extends React.Component {
|
class RoomInfoEditView extends React.Component {
|
||||||
static navigationOptions = () => ({
|
static navigationOptions = () => ({
|
||||||
|
@ -48,6 +50,7 @@ class RoomInfoEditView extends React.Component {
|
||||||
})
|
})
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
navigation: PropTypes.object,
|
||||||
route: PropTypes.object,
|
route: PropTypes.object,
|
||||||
deleteRoom: PropTypes.func,
|
deleteRoom: PropTypes.func,
|
||||||
serverVersion: PropTypes.string,
|
serverVersion: PropTypes.string,
|
||||||
|
@ -58,7 +61,8 @@ class RoomInfoEditView extends React.Component {
|
||||||
archiveRoomPermission: PropTypes.array,
|
archiveRoomPermission: PropTypes.array,
|
||||||
unarchiveRoomPermission: PropTypes.array,
|
unarchiveRoomPermission: PropTypes.array,
|
||||||
deleteCPermission: PropTypes.array,
|
deleteCPermission: PropTypes.array,
|
||||||
deletePPermission: PropTypes.array
|
deletePPermission: PropTypes.array,
|
||||||
|
editTeamChannelPermission: PropTypes.array
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -100,7 +104,8 @@ class RoomInfoEditView extends React.Component {
|
||||||
archiveRoomPermission,
|
archiveRoomPermission,
|
||||||
unarchiveRoomPermission,
|
unarchiveRoomPermission,
|
||||||
deleteCPermission,
|
deleteCPermission,
|
||||||
deletePPermission
|
deletePPermission,
|
||||||
|
editTeamChannelPermission
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const rid = route.params?.rid;
|
const rid = route.params?.rid;
|
||||||
if (!rid) {
|
if (!rid) {
|
||||||
|
@ -116,7 +121,32 @@ class RoomInfoEditView extends React.Component {
|
||||||
this.init(this.room);
|
this.init(this.room);
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await RocketChat.hasPermission([
|
let result;
|
||||||
|
|
||||||
|
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,
|
setReadOnlyPermission,
|
||||||
setReactWhenReadOnlyPermission,
|
setReactWhenReadOnlyPermission,
|
||||||
archiveRoomPermission,
|
archiveRoomPermission,
|
||||||
|
@ -135,6 +165,7 @@ class RoomInfoEditView extends React.Component {
|
||||||
[PERMISSION_DELETE_P]: result[5]
|
[PERMISSION_DELETE_P]: result[5]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log(e);
|
log(e);
|
||||||
}
|
}
|
||||||
|
@ -284,13 +315,37 @@ class RoomInfoEditView extends React.Component {
|
||||||
}, 100);
|
}, 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 = () => {
|
delete = () => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { deleteRoom } = this.props;
|
const { deleteRoom } = this.props;
|
||||||
|
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
I18n.t('Are_you_sure_question_mark'),
|
I18n.t('Confirmation'),
|
||||||
I18n.t('Delete_Room_Warning'),
|
I18n.t('Delete_Team_Warning'),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
text: I18n.t('Cancel'),
|
text: I18n.t('Cancel'),
|
||||||
|
@ -299,7 +354,7 @@ class RoomInfoEditView extends React.Component {
|
||||||
{
|
{
|
||||||
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
|
text: I18n.t('Yes_action_it', { action: I18n.t('delete') }),
|
||||||
style: 'destructive',
|
style: 'destructive',
|
||||||
onPress: () => deleteRoom(room.rid, room.t)
|
onPress: () => (this.room.teamId ? this.deleteTeam(room.name) : deleteRoom(room.rid, room.t))
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
{ cancelable: false }
|
{ cancelable: false }
|
||||||
|
@ -678,7 +733,8 @@ const mapStateToProps = state => ({
|
||||||
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
|
archiveRoomPermission: state.permissions[PERMISSION_ARCHIVE],
|
||||||
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
|
unarchiveRoomPermission: state.permissions[PERMISSION_UNARCHIVE],
|
||||||
deleteCPermission: state.permissions[PERMISSION_DELETE_C],
|
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 => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { FlatList } from 'react-native';
|
import { FlatList, Alert } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import * as List from '../../containers/List';
|
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_OWNER = 'set-owner';
|
||||||
const PERMISSION_SET_MODERATOR = 'set-moderator';
|
const PERMISSION_SET_MODERATOR = 'set-moderator';
|
||||||
const PERMISSION_REMOVE_USER = 'remove-user';
|
const PERMISSION_REMOVE_USER = 'remove-user';
|
||||||
|
const PERMISSION_EDIT_TEAM_MEMBER = 'edit-team-member';
|
||||||
|
|
||||||
class RoomMembersView extends React.Component {
|
class RoomMembersView extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -55,7 +56,8 @@ class RoomMembersView extends React.Component {
|
||||||
setLeaderPermission: PropTypes.array,
|
setLeaderPermission: PropTypes.array,
|
||||||
setOwnerPermission: PropTypes.array,
|
setOwnerPermission: PropTypes.array,
|
||||||
setModeratorPermission: PropTypes.array,
|
setModeratorPermission: PropTypes.array,
|
||||||
removeUserPermission: PropTypes.array
|
removeUserPermission: PropTypes.array,
|
||||||
|
editTeamMemberPermission: PropTypes.array
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -94,9 +96,25 @@ class RoomMembersView extends React.Component {
|
||||||
|
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const {
|
const {
|
||||||
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission, editTeamMemberPermission
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const result = await RocketChat.hasPermission([
|
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
|
muteUserPermission, setLeaderPermission, setOwnerPermission, setModeratorPermission, removeUserPermission
|
||||||
], room.rid);
|
], room.rid);
|
||||||
|
|
||||||
|
@ -107,6 +125,16 @@ class RoomMembersView extends React.Component {
|
||||||
[PERMISSION_SET_MODERATOR]: result[3],
|
[PERMISSION_SET_MODERATOR]: result[3],
|
||||||
[PERMISSION_REMOVE_USER]: result[4]
|
[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_EDIT_TEAM_MEMBER]: result[5]
|
||||||
|
};
|
||||||
|
|
||||||
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
const hasSinglePermission = Object.values(this.permissions).some(p => !!p);
|
||||||
if (hasSinglePermission) {
|
if (hasSinglePermission) {
|
||||||
|
@ -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) => {
|
onPressUser = (selectedUser) => {
|
||||||
const { room } = this.state;
|
const { room } = this.state;
|
||||||
const { showActionSheet, user } = this.props;
|
const { showActionSheet, user } = this.props;
|
||||||
|
@ -173,6 +251,46 @@ class RoomMembersView extends React.Component {
|
||||||
onPress: () => this.navToDirectMessage(selectedUser)
|
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
|
// Owner
|
||||||
if (this.permissions['set-owner']) {
|
if (this.permissions['set-owner']) {
|
||||||
const userRoleResult = this.roomRoles.find(r => r.u._id === selectedUser._id);
|
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
|
// Remove from room
|
||||||
if (this.permissions['remove-user']) {
|
if (this.permissions['remove-user']) {
|
||||||
options.push({
|
options.push({
|
||||||
|
@ -292,6 +380,7 @@ class RoomMembersView extends React.Component {
|
||||||
this.setState({ isLoading: true });
|
this.setState({ isLoading: true });
|
||||||
try {
|
try {
|
||||||
const membersResult = await RocketChat.getRoomMembers(rid, allUsers, members.length, PAGE_SIZE);
|
const membersResult = await RocketChat.getRoomMembers(rid, allUsers, members.length, PAGE_SIZE);
|
||||||
|
console.log({ membersResult });
|
||||||
const newMembers = membersResult.records;
|
const newMembers = membersResult.records;
|
||||||
this.setState({
|
this.setState({
|
||||||
members: members.concat(newMembers || []),
|
members: members.concat(newMembers || []),
|
||||||
|
@ -477,7 +566,8 @@ const mapStateToProps = state => ({
|
||||||
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
|
setLeaderPermission: state.permissions[PERMISSION_SET_LEADER],
|
||||||
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
|
setOwnerPermission: state.permissions[PERMISSION_SET_OWNER],
|
||||||
setModeratorPermission: state.permissions[PERMISSION_SET_MODERATOR],
|
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)));
|
export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView)));
|
||||||
|
|
|
@ -18,7 +18,8 @@ class RightButtonsContainer extends Component {
|
||||||
teamId: PropTypes.bool,
|
teamId: PropTypes.bool,
|
||||||
navigation: PropTypes.object,
|
navigation: PropTypes.object,
|
||||||
isMasterDetail: PropTypes.bool,
|
isMasterDetail: PropTypes.bool,
|
||||||
toggleFollowThread: PropTypes.func
|
toggleFollowThread: PropTypes.func,
|
||||||
|
joined: PropTypes.bool
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -113,7 +114,7 @@ class RightButtonsContainer extends Component {
|
||||||
goTeamChannels = () => {
|
goTeamChannels = () => {
|
||||||
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
logEvent(events.ROOM_GO_TEAM_CHANNELS);
|
||||||
const {
|
const {
|
||||||
navigation, isMasterDetail, teamId
|
navigation, isMasterDetail, teamId, rid
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (isMasterDetail) {
|
if (isMasterDetail) {
|
||||||
navigation.navigate('ModalStackNavigator', {
|
navigation.navigate('ModalStackNavigator', {
|
||||||
|
@ -121,7 +122,7 @@ class RightButtonsContainer extends Component {
|
||||||
params: { teamId }
|
params: { teamId }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
navigation.navigate('TeamChannelsView', { teamId });
|
navigation.navigate('TeamChannelsView', { teamId, rid });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,7 +164,7 @@ class RightButtonsContainer extends Component {
|
||||||
isFollowingThread, tunread, tunreadUser, tunreadGroup
|
isFollowingThread, tunread, tunreadUser, tunreadGroup
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
t, tmid, threadsEnabled, teamId
|
t, tmid, threadsEnabled, teamId, joined
|
||||||
} = this.props;
|
} = this.props;
|
||||||
if (t === 'l') {
|
if (t === 'l') {
|
||||||
return null;
|
return null;
|
||||||
|
@ -181,7 +182,7 @@ class RightButtonsContainer extends Component {
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<HeaderButton.Container>
|
<HeaderButton.Container>
|
||||||
{teamId ? (
|
{teamId && joined ? (
|
||||||
<HeaderButton.Item
|
<HeaderButton.Item
|
||||||
iconName='channel-public'
|
iconName='channel-public'
|
||||||
onPress={this.goTeamChannels}
|
onPress={this.goTeamChannels}
|
||||||
|
|
|
@ -301,7 +301,7 @@ class RoomView extends React.Component {
|
||||||
|
|
||||||
setHeader = () => {
|
setHeader = () => {
|
||||||
const {
|
const {
|
||||||
room, unreadsCount, roomUserId
|
room, unreadsCount, roomUserId, joined
|
||||||
} = this.state;
|
} = this.state;
|
||||||
const {
|
const {
|
||||||
navigation, isMasterDetail, theme, baseUrl, user, insets, route
|
navigation, isMasterDetail, theme, baseUrl, user, insets, route
|
||||||
|
@ -331,7 +331,7 @@ class RoomView extends React.Component {
|
||||||
let numIconsRight = 2;
|
let numIconsRight = 2;
|
||||||
if (tmid) {
|
if (tmid) {
|
||||||
numIconsRight = 1;
|
numIconsRight = 1;
|
||||||
} else if (teamId) {
|
} else if (teamId && joined) {
|
||||||
numIconsRight = 3;
|
numIconsRight = 3;
|
||||||
}
|
}
|
||||||
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight });
|
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight });
|
||||||
|
@ -380,6 +380,7 @@ class RoomView extends React.Component {
|
||||||
rid={rid}
|
rid={rid}
|
||||||
tmid={tmid}
|
tmid={tmid}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
|
joined={joined}
|
||||||
t={t}
|
t={t}
|
||||||
navigation={navigation}
|
navigation={navigation}
|
||||||
toggleFollowThread={this.toggleFollowThread}
|
toggleFollowThread={this.toggleFollowThread}
|
||||||
|
@ -1032,6 +1033,7 @@ class RoomView extends React.Component {
|
||||||
renderActions = () => {
|
renderActions = () => {
|
||||||
const { room, readOnly } = this.state;
|
const { room, readOnly } = this.state;
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MessageActions
|
<MessageActions
|
||||||
|
|
|
@ -894,7 +894,6 @@ class RoomsListView extends React.Component {
|
||||||
return this.renderSectionHeader(item.rid);
|
return this.renderSectionHeader(item.rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { item: currentItem } = this.state;
|
|
||||||
const {
|
const {
|
||||||
user: { username },
|
user: { username },
|
||||||
StoreLastMessage,
|
StoreLastMessage,
|
||||||
|
@ -925,7 +924,6 @@ class RoomsListView extends React.Component {
|
||||||
getIsGroupChat={this.isGroupChat}
|
getIsGroupChat={this.isGroupChat}
|
||||||
getIsRead={this.isRead}
|
getIsRead={this.isRead}
|
||||||
visitor={item.visitor}
|
visitor={item.visitor}
|
||||||
isFocused={currentItem?.rid === item.rid}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -948,7 +946,6 @@ class RoomsListView extends React.Component {
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <ActivityIndicator theme={theme} />;
|
return <ActivityIndicator theme={theme} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
ref={this.getScrollRef}
|
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) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.init();
|
this.init();
|
||||||
|
this.flatlist = React.createRef();
|
||||||
const maxUsers = props.route.params?.maxUsers;
|
const maxUsers = props.route.params?.maxUsers;
|
||||||
this.state = {
|
this.state = {
|
||||||
maxUsers,
|
maxUsers,
|
||||||
|
@ -190,9 +190,13 @@ class SelectedUsersView extends React.Component {
|
||||||
if (users.length === 0) {
|
if (users.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
const ITEM_WIDTH = 250;
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<FlatList
|
||||||
data={users}
|
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}
|
keyExtractor={item => item._id}
|
||||||
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
|
style={[sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}
|
||||||
contentContainerStyle={{ marginVertical: 5 }}
|
contentContainerStyle={{ marginVertical: 5 }}
|
||||||
|
|
|
@ -15,7 +15,7 @@ const Item = React.memo(({
|
||||||
testID={testID}
|
testID={testID}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
theme={theme}
|
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}>
|
<View style={styles.itemHorizontal}>
|
||||||
{left}
|
{left}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Keyboard } from 'react-native';
|
import { Keyboard, Alert } from 'react-native';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { Q } from '@nozbe/watermelondb';
|
import { Q } from '@nozbe/watermelondb';
|
||||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||||
|
@ -28,6 +28,8 @@ import debounce from '../utils/debounce';
|
||||||
import { showErrorAlert } from '../utils/info';
|
import { showErrorAlert } from '../utils/info';
|
||||||
import { goRoom } from '../utils/goRoom';
|
import { goRoom } from '../utils/goRoom';
|
||||||
import I18n from '../i18n';
|
import I18n from '../i18n';
|
||||||
|
import { withActionSheet } from '../containers/ActionSheet';
|
||||||
|
import { deleteRoom as deleteRoomAction } from '../actions/room';
|
||||||
|
|
||||||
const API_FETCH_COUNT = 25;
|
const API_FETCH_COUNT = 25;
|
||||||
|
|
||||||
|
@ -47,12 +49,17 @@ class TeamChannelsView extends React.Component {
|
||||||
theme: PropTypes.string,
|
theme: PropTypes.string,
|
||||||
useRealName: PropTypes.bool,
|
useRealName: PropTypes.bool,
|
||||||
width: PropTypes.number,
|
width: PropTypes.number,
|
||||||
StoreLastMessage: PropTypes.bool
|
StoreLastMessage: PropTypes.bool,
|
||||||
|
addTeamChannelPermission: PropTypes.array,
|
||||||
|
removeTeamChannelPermission: PropTypes.array,
|
||||||
|
showActionSheet: PropTypes.func,
|
||||||
|
deleteRoom: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.teamId = props.route.params?.teamId;
|
this.teamId = props.route.params?.teamId;
|
||||||
|
this.rid = props.route.params?.rid;
|
||||||
this.state = {
|
this.state = {
|
||||||
loading: true,
|
loading: true,
|
||||||
loadingMore: false,
|
loadingMore: false,
|
||||||
|
@ -60,9 +67,11 @@ class TeamChannelsView extends React.Component {
|
||||||
isSearching: false,
|
isSearching: false,
|
||||||
searchText: '',
|
searchText: '',
|
||||||
search: [],
|
search: [],
|
||||||
end: false
|
end: false,
|
||||||
|
showCreate: false
|
||||||
};
|
};
|
||||||
this.loadTeam();
|
this.loadTeam();
|
||||||
|
this.setHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -70,6 +79,9 @@ class TeamChannelsView extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
loadTeam = async() => {
|
loadTeam = async() => {
|
||||||
|
const { addTeamChannelPermission } = this.props;
|
||||||
|
const { loading } = this.state;
|
||||||
|
|
||||||
const db = database.active;
|
const db = database.active;
|
||||||
try {
|
try {
|
||||||
const subCollection = db.get('subscriptions');
|
const subCollection = db.get('subscriptions');
|
||||||
|
@ -82,6 +94,14 @@ class TeamChannelsView extends React.Component {
|
||||||
if (!this.team) {
|
if (!this.team) {
|
||||||
throw new Error();
|
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 {
|
} catch {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.pop();
|
navigation.pop();
|
||||||
|
@ -135,8 +155,8 @@ class TeamChannelsView extends React.Component {
|
||||||
}
|
}
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
getHeader = () => {
|
setHeader = () => {
|
||||||
const { isSearching } = this.state;
|
const { isSearching, showCreate, data } = this.state;
|
||||||
const {
|
const {
|
||||||
navigation, isMasterDetail, insets, theme
|
navigation, isMasterDetail, insets, theme
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
@ -146,7 +166,7 @@ class TeamChannelsView extends React.Component {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 1 });
|
const headerTitlePosition = getHeaderTitlePosition({ insets, numIconsRight: 2 });
|
||||||
|
|
||||||
if (isSearching) {
|
if (isSearching) {
|
||||||
return {
|
return {
|
||||||
|
@ -201,14 +221,11 @@ class TeamChannelsView extends React.Component {
|
||||||
options.headerRight = () => (
|
options.headerRight = () => (
|
||||||
<HeaderButton.Container>
|
<HeaderButton.Container>
|
||||||
<HeaderButton.Item iconName='search' onPress={this.onSearchPress} />
|
<HeaderButton.Item iconName='search' onPress={this.onSearchPress} />
|
||||||
|
{ showCreate
|
||||||
|
? <HeaderButton.Item iconName='create' onPress={() => navigation.navigate('AddChannelTeamView', { teamId: this.teamId, teamChannels: data })} />
|
||||||
|
: null}
|
||||||
</HeaderButton.Container>
|
</HeaderButton.Container>
|
||||||
);
|
);
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
setHeader = () => {
|
|
||||||
const { navigation } = this.props;
|
|
||||||
const options = this.getHeader();
|
|
||||||
navigation.setOptions(options);
|
navigation.setOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +304,93 @@ class TeamChannelsView extends React.Component {
|
||||||
}
|
}
|
||||||
}, 1000, true);
|
}, 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 }) => {
|
renderItem = ({ item }) => {
|
||||||
const {
|
const {
|
||||||
StoreLastMessage,
|
StoreLastMessage,
|
||||||
|
@ -302,6 +406,7 @@ class TeamChannelsView extends React.Component {
|
||||||
showLastMessage={StoreLastMessage}
|
showLastMessage={StoreLastMessage}
|
||||||
onPress={this.onPressItem}
|
onPress={this.onPressItem}
|
||||||
width={width}
|
width={width}
|
||||||
|
onLongPress={this.showChannelActions}
|
||||||
useRealName={useRealName}
|
useRealName={useRealName}
|
||||||
getRoomTitle={this.getRoomTitle}
|
getRoomTitle={this.getRoomTitle}
|
||||||
getRoomAvatar={this.getRoomAvatar}
|
getRoomAvatar={this.getRoomAvatar}
|
||||||
|
@ -365,7 +470,13 @@ const mapStateToProps = state => ({
|
||||||
user: getUserSelector(state),
|
user: getUserSelector(state),
|
||||||
useRealName: state.settings.UI_Use_Real_Name,
|
useRealName: state.settings.UI_Use_Real_Name,
|
||||||
isMasterDetail: state.app.isMasterDetail,
|
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(({
|
const DropdownItem = React.memo(({
|
||||||
theme, onPress, iconName, text
|
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}>
|
<View style={styles.container}>
|
||||||
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{text}</Text>
|
<Text style={[styles.text, { color: themes[theme].auxiliaryText }]}>{text}</Text>
|
||||||
{iconName ? <CustomIcon name={iconName} size={22} color={themes[theme].auxiliaryText} /> : null}
|
{iconName ? <CustomIcon name={iconName} size={22} color={themes[theme].auxiliaryText} /> : null}
|
||||||
|
|
|
@ -42,6 +42,11 @@ const data = {
|
||||||
name: `detox-private-${ value }`
|
name: `detox-private-${ value }`
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
teams: {
|
||||||
|
private: {
|
||||||
|
name: `detox-team-${ value }`
|
||||||
|
}
|
||||||
|
},
|
||||||
registeringUser: {
|
registeringUser: {
|
||||||
username: `newuser${ value }`,
|
username: `newuser${ value }`,
|
||||||
password: `password${ 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) => {
|
const createGroupIfNotExists = async (groupname) => {
|
||||||
console.log(`Creating private group ${groupname}`)
|
console.log(`Creating private group ${groupname}`)
|
||||||
try {
|
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
|
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