diff --git a/__tests__/__snapshots__/Storyshots.test.js.snap b/__tests__/__snapshots__/Storyshots.test.js.snap
index 52cde22be..4c88d8e5c 100644
--- a/__tests__/__snapshots__/Storyshots.test.js.snap
+++ b/__tests__/__snapshots__/Storyshots.test.js.snap
@@ -44348,589 +44348,180 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
-
-
-
-
-
-
-
-
-
-
-
- Read
-
-
-
-
-
-
-
-
-
-
- Favorite
-
-
-
-
-
-
-
- Hide
-
-
-
-
-
-
-
+ >
+
+
-
-
-
-
-
-
- unread
-
+
+
+
- 1
+ rocket.cat
@@ -45165,202 +44756,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- unread
-
+
+
+
- +999
+ unread
+
+
+ 1
+
+
@@ -45594,202 +45205,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- user mentions
-
+
+
+
- 1
+ unread
+
+
+ +999
+
+
@@ -46023,202 +45654,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- group mentions
-
+
+
+
- 1
+ user mentions
+
+
+ 1
+
+
@@ -46452,202 +46103,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- thread unread
-
+
+
+
- 1
+ group mentions
+
+
+ 1
+
+
@@ -46881,202 +46552,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- thread unread user
-
+
+
+
- 1
+ thread unread
+
+
+ 1
+
+
@@ -47310,202 +47001,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- thread unread group
-
+
+
+
- 1
+ thread unread user
+
+
+ 1
+
+
@@ -47739,202 +47450,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- user mentions priority 1
-
+
+
+
- 1
+ thread unread group
+
+
+ 1
+
+
@@ -48168,202 +47899,222 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
-
-
-
-
-
-
- group mentions priority 2
-
+
+
+
- 1
+ user mentions priority 1
+
+
+ 1
+
+
@@ -48597,202 +48348,671 @@ exports[`Storyshots Room Item Alerts 1`] = `
}
>
-
+ >
+
+
+
+
+
+
+
+
+
+ group mentions priority 2
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Read
+
+
+
+
+
+
+
+
+
+
+ Favorite
+
+
+
+
+
+
+
+ Hide
+
+
+
+
+
+
-
-
-
-
- thread unread priority 3
-
+
+
+
+
+
+
+
- 1
+ thread unread priority 3
+
+
+ 1
+
+
@@ -49039,157 +49259,177 @@ exports[`Storyshots Room Item Basic 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -49435,2020 +49675,173 @@ exports[`Storyshots Room Item Last Message 1`] = `
}
>
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- No Message
-
-
-
-
-
-
-
-
-
-
-
-
-
- Read
-
-
-
-
-
-
-
-
-
-
- Favorite
-
-
-
-
-
-
-
- Hide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- 2
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Read
-
-
-
-
-
-
-
-
-
-
- Favorite
-
-
-
-
-
-
-
- Hide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- You: 1
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Read
-
-
-
-
-
-
-
-
-
-
- Favorite
-
-
-
-
-
-
-
- Hide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Read
-
-
-
-
-
-
-
-
-
-
- Favorite
-
-
-
-
-
-
-
- Hide
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
-
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
- 1
+ No Message
@@ -51701,252 +50137,173 @@ exports[`Storyshots Room Item Last Message 1`] = `
}
>
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
-
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
- +999
+ 2
@@ -52199,252 +50599,173 @@ exports[`Storyshots Room Item Last Message 1`] = `
}
>
-
-
-
-
-
-
-
-
-
- rocket.cat
-
-
- 10:00
-
-
-
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
-
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
- 1
+ You: 1
@@ -52470,6 +50834,2022 @@ exports[`Storyshots Room Item Last Message 1`] = `
+
+
+
+
+
+
+
+
+ Read
+
+
+
+
+
+
+
+
+
+
+ Favorite
+
+
+
+
+
+
+
+ Hide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Read
+
+
+
+
+
+
+
+
+
+
+ Favorite
+
+
+
+
+
+
+
+ Hide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Read
+
+
+
+
+
+
+
+
+
+
+ Favorite
+
+
+
+
+
+
+
+ Hide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
+
+
+
+ +999
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Read
+
+
+
+
+
+
+
+
+
+
+ Favorite
+
+
+
+
+
+
+
+ Hide
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ rocket.cat
+
+
+ 10:00
+
+
+
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
+
+
+
+ 1
+
+
+
+
+
+
+
+
`;
@@ -52710,157 +53090,177 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -53093,145 +53493,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -53464,145 +53884,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -53835,145 +54275,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -54206,145 +54666,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -54577,145 +55057,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -54948,145 +55448,165 @@ exports[`Storyshots Room Item Type 1`] = `
}
>
-
+ >
+
+
-
-
-
+
+
+
+
-
-
-
- rocket.cat
-
+ ]
+ }
+ >
+ rocket.cat
+
+
@@ -55332,157 +55852,177 @@ exports[`Storyshots Room Item User 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- diego.mello
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ diego.mello
+
+
@@ -55715,157 +56255,177 @@ exports[`Storyshots Room Item User 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries
+
+
@@ -56111,157 +56671,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -56494,157 +57074,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -56877,157 +57477,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -57260,157 +57880,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -57643,157 +58283,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
@@ -58026,157 +58686,177 @@ exports[`Storyshots Room Item User status 1`] = `
}
>
-
+ >
+
+
-
-
-
+
-
-
-
- rocket.cat
-
+ Object {
+ "fontFamily": "custom",
+ "fontStyle": "normal",
+ "fontWeight": "normal",
+ },
+ Object {},
+ ]
+ }
+ >
+
+
+
+ rocket.cat
+
+
diff --git a/app/i18n/locales/en.json b/app/i18n/locales/en.json
index 06e3167eb..2854728ab 100644
--- a/app/i18n/locales/en.json
+++ b/app/i18n/locales/en.json
@@ -719,5 +719,8 @@
"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": "Add Existing",
+ "Add_Existing_Channel": "Add Existing Channel",
+ "Remove_from_Team": "Remove from Team",
+ "Auto-join": "Auto-join"
}
diff --git a/app/lib/methods/getPermissions.js b/app/lib/methods/getPermissions.js
index 09b91aa63..1f4bcb9cb 100644
--- a/app/lib/methods/getPermissions.js
+++ b/app/lib/methods/getPermissions.js
@@ -13,6 +13,7 @@ const PERMISSIONS = [
'add-user-to-any-c-room',
'add-user-to-any-p-room',
'add-user-to-joined-room',
+ 'add-team-channel',
'archive-room',
'auto-translate',
'create-invite-links',
@@ -21,11 +22,13 @@ const PERMISSIONS = [
'delete-p',
'edit-message',
'edit-room',
+ 'edit-team-channel',
'force-delete-message',
'mute-user',
'pin-message',
'post-readonly',
'remove-user',
+ 'remove-team-channel',
'set-leader',
'set-moderator',
'set-owner',
@@ -38,7 +41,9 @@ const PERMISSIONS = [
'view-privileged-setting',
'view-room-administration',
'view-statistics',
- 'view-user-administration'
+ 'view-user-administration',
+ 'view-all-teams',
+ 'view-all-team-channels'
];
export async function setPermissions() {
diff --git a/app/lib/rocketchat.js b/app/lib/rocketchat.js
index 965f1c211..b98ab4958 100644
--- a/app/lib/rocketchat.js
+++ b/app/lib/rocketchat.js
@@ -734,7 +734,7 @@ const RocketChat = {
const params = {
name,
users,
- type: type ? 1 : 0,
+ type,
room: {
readOnly,
extraData: {
@@ -746,6 +746,22 @@ const RocketChat = {
// RC 3.13.0
return this.post('teams.create', params);
},
+ addTeamRooms({ rooms, teamId }) {
+ const params = {
+ rooms: rooms.length ? [...rooms] : [rooms],
+ teamId
+ };
+ // RC 3.13.0
+ return this.post('teams.addRooms', params);
+ },
+ deleteTeamRoom({ rid, teamId }) {
+ const params = {
+ roomId: rid,
+ teamId
+ };
+ // RC 3.13.0
+ return this.post('teams.removeRoom', params);
+ },
joinRoom(roomId, joinCode, type) {
// TODO: join code
// RC 0.48.0
diff --git a/app/presentation/RoomItem/RoomItem.js b/app/presentation/RoomItem/RoomItem.js
index b3922787b..6d8c31c20 100644
--- a/app/presentation/RoomItem/RoomItem.js
+++ b/app/presentation/RoomItem/RoomItem.js
@@ -42,6 +42,7 @@ const RoomItem = ({
testID,
swipeEnabled,
onPress,
+ onLongPress,
toggleFav,
toggleRead,
hideChannel,
@@ -49,6 +50,7 @@ const RoomItem = ({
}) => (
{
+ const { rowState } = this.state;
+ if (rowState !== 0) {
+ this.close();
+ return;
+ }
+ const { onLongPress } = this.props;
+ if (onLongPress) {
+ onLongPress();
+ }
+ };
+
render() {
const {
testID, isRead, width, favorite, children, theme, isFocused, swipeEnabled
@@ -237,8 +249,9 @@ class Touchable extends React.Component {
transform: [{ translateX: this.transX }]
}}
>
-
{children}
-
+
diff --git a/app/presentation/RoomItem/index.js b/app/presentation/RoomItem/index.js
index 80bcf063b..ee131867d 100644
--- a/app/presentation/RoomItem/index.js
+++ b/app/presentation/RoomItem/index.js
@@ -25,6 +25,7 @@ class RoomItemContainer extends React.Component {
showLastMessage: PropTypes.bool,
id: PropTypes.string,
onPress: PropTypes.func,
+ onLongPress: PropTypes.func,
username: PropTypes.string,
avatarSize: PropTypes.number,
width: PropTypes.number,
@@ -112,6 +113,11 @@ class RoomItemContainer extends React.Component {
return onPress(item);
}
+ onLongPress = () => {
+ const { item, onLongPress } = this.props;
+ return onLongPress(item);
+ }
+
render() {
const {
item,
@@ -160,6 +166,7 @@ class RoomItemContainer extends React.Component {
isGroupChat={this.isGroupChat}
isRead={isRead}
onPress={this.onPress}
+ onLongPress={this.onLongPress}
date={date}
accessibilityLabel={accessibilityLabel}
width={width}
diff --git a/app/sagas/createChannel.js b/app/sagas/createChannel.js
index f8e96aeb1..5cd1c5d0c 100644
--- a/app/sagas/createChannel.js
+++ b/app/sagas/createChannel.js
@@ -25,6 +25,10 @@ const createTeam = function createTeam(data) {
return RocketChat.createTeam(data);
};
+const addTeamRoom = function addRoomToTeam(params) {
+ return RocketChat.addTeamRoom(params);
+};
+
const handleRequest = function* handleRequest({ data }) {
try {
const auth = yield select(state => state.login.isAuthenticated);
@@ -40,6 +44,7 @@ const handleRequest = function* handleRequest({ data }) {
broadcast,
encrypted
} = data;
+ // TODO: Create event CT_CREATE
logEvent(events.CR_CREATE, {
type,
readOnly,
@@ -67,14 +72,22 @@ const handleRequest = function* handleRequest({ data }) {
encrypted
});
sub = yield call(createChannel, data);
- }
+ if (data.teamId) {
+ // TODO: Log when adding room to team
+ const channels = yield call(addTeamRoom, { rooms: sub.rid, teamId: data.teamId });
+ if (channels.success) {
+ sub.teamId = channels.teamId;
+ sub.isTeamChannel = true;
+ }
+ }
+ }
try {
const db = database.active;
const subCollection = db.get('subscriptions');
yield db.action(async() => {
await subCollection.create((s) => {
- s._raw = sanitizedRaw({ id: sub.team ? sub.team.roomId : 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);
});
});
diff --git a/app/stacks/InsideStack.js b/app/stacks/InsideStack.js
index 9052b1310..75758960b 100644
--- a/app/stacks/InsideStack.js
+++ b/app/stacks/InsideStack.js
@@ -72,6 +72,7 @@ import CreateDiscussionView from '../views/CreateDiscussionView';
import QueueListView from '../ee/omnichannel/views/QueueListView';
import AddChannelTeamView from '../views/AddChannelTeamView';
+import AddExistingChannelView from '../views/AddExistingChannelView';
// ChatsStackNavigator
const ChatsStack = createStackNavigator();
@@ -180,6 +181,11 @@ const ChatsStackNavigator = () => {
component={AddChannelTeamView}
options={AddChannelTeamView.navigationOptions}
/>
+
{
navigationMethod = Navigation.replace;
}
- navigationMethod('RoomView', {
- rid: item.roomId || item.rid,
- name: RocketChat.getRoomTitle(item),
- t: item.type ? 'p' : item.t,
- prid: item.prid,
- room: item,
- search: item.search,
- visitor: item.visitor,
- roomUserId: RocketChat.getUidDirectMessage(item),
- ...props
- });
+ if (item.isTeamChannel) {
+ // TODO: Refactor
+ Navigation.navigate('TeamChannelsView');
+ Navigation.push('RoomView', {
+ rid: item.roomId || item.rid,
+ name: RocketChat.getRoomTitle(item),
+ t: item.type ? 'p' : item.t,
+ prid: item.prid,
+ room: item,
+ search: item.search,
+ visitor: item.visitor,
+ roomUserId: RocketChat.getUidDirectMessage(item),
+ teamId: item.teamId,
+ ...props
+ });
+ } else {
+ navigationMethod('RoomView', {
+ rid: item.roomId || item.rid,
+ name: RocketChat.getRoomTitle(item),
+ t: item.type ? 'p' : item.t,
+ prid: item.prid,
+ room: item,
+ search: item.search,
+ visitor: item.visitor,
+ roomUserId: RocketChat.getUidDirectMessage(item),
+ ...props
+ });
+ }
};
export const goRoom = async({ item = {}, isMasterDetail = false, ...props }) => {
diff --git a/app/utils/touch.js b/app/utils/touch.js
index d5c22d4df..ae9d01bb0 100644
--- a/app/utils/touch.js
+++ b/app/utils/touch.js
@@ -15,13 +15,14 @@ class Touch extends React.Component {
render() {
const {
- children, onPress, theme, underlayColor, ...props
+ children, onPress, onLongPress, theme, underlayColor, ...props
} = this.props;
return (
(
-
- )
+ headerTitle: I18n.t('Add_Channel_to_Team')
};
if (isMasterDetail) {
@@ -74,7 +71,7 @@ class AddChannelTeamView extends React.Component {
return (
onPress()}
+ onPress={onPress}
style={{ backgroundColor: themes[theme].backgroundColor }}
testID={testID}
theme={theme}
@@ -88,21 +85,22 @@ class AddChannelTeamView extends React.Component {
}
render() {
- const { navigation } = this.props;
+ const { navigation, route } = this.props;
+ const { teamChannels } = route?.params;
return (
{this.renderButton({
- onPress: navigation.navigate('NewMessageStackNavigator', { screen: 'CreateChannelView', isTeam: false }),
+ 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'),
+ onPress: () => navigation.navigate('AddExistingChannelView', { teamId: this.teamId, teamChannels }),
title: I18n.t('Add_Existing'),
icon: 'team',
testID: 'add-channel-team-view-create-channel'
diff --git a/app/views/AddExistingChannelView.js b/app/views/AddExistingChannelView.js
new file mode 100644
index 000000000..d8c4da808
--- /dev/null
+++ b/app/views/AddExistingChannelView.js
@@ -0,0 +1,256 @@
+/* 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 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
+ };
+
+ 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 = () => ;
+ } else {
+ options.headerLeft = () => navigation.pop()} tintColor={themes[theme].headerTintColor} />;
+ }
+
+ options.headerRight = () => selected.length > 0 && (
+
+
+
+ );
+
+ navigation.setOptions(options);
+ }
+
+ // eslint-disable-next-line react/sort-comp
+ init = async() => {
+ try {
+ const db = database.active;
+ const channels = await db.collections
+ .get('subscriptions')
+ .query(
+ Q.where('t', 'p'),
+ Q.where('team_id', ''),
+ Q.experimentalTake(QUERY_SIZE),
+ Q.experimentalSortBy('room_updated_at', Q.desc)
+ )
+ .fetch();
+ this.setState({ channels });
+ } 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 {
+ // TODO: Log request
+ const result = await RocketChat.addTeamRooms({ rooms: selected, teamId: this.teamId });
+ if (result.success) {
+ this.setState({ loading: false });
+ goRoom(result, isMasterDetail);
+ }
+ } catch (e) {
+ // TODO: Log error
+ this.setState({ loading: false });
+ }
+ }
+
+ renderChannel = ({
+ onPress, testID, title, icon, checked
+ }) => {
+ const { theme } = this.props;
+ return (
+
+
+
+
+ {title}
+
+ {checked ? : null}
+
+
+ );
+ }
+
+ renderHeader = () => {
+ const { theme } = this.props;
+ return (
+
+ this.onSearchChangeText(text)} testID='add-existing-channel-view-search' />
+
+ );
+ }
+
+ 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 (
+ 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 (
+
+
+ {this.renderList()}
+
+
+ );
+ }
+}
+
+const mapStateToProps = state => ({
+ isMasterDetail: state.app.isMasterDetail
+});
+
+export default connect(mapStateToProps, null)(withTheme(AddExistingChannelView));
diff --git a/app/views/CreateChannelView.js b/app/views/CreateChannelView.js
index 54d70d80b..de2804c33 100644
--- a/app/views/CreateChannelView.js
+++ b/app/views/CreateChannelView.js
@@ -87,13 +87,15 @@ class CreateChannelView extends React.Component {
id: PropTypes.string,
token: PropTypes.string
}),
- theme: PropTypes.string
+ theme: PropTypes.string,
+ teamId: PropTypes.string
};
constructor(props) {
super(props);
const { route } = this.props;
this.isTeam = route?.params?.isTeam || false;
+ this.teamId = route?.params?.teamId;
this.state = {
channelName: '',
type: true,
@@ -173,7 +175,7 @@ class CreateChannelView extends React.Component {
// create channel or team
create({
- name: channelName, users, type, readOnly, broadcast, encrypted, isTeam: this.isTeam
+ name: channelName, users, type, readOnly, broadcast, encrypted, isTeam: this.isTeam, teamId: this.teamId
});
Review.pushPositiveEvent();
diff --git a/app/views/RoomView/RightButtons.js b/app/views/RoomView/RightButtons.js
index 398f868b8..c2c1a43ae 100644
--- a/app/views/RoomView/RightButtons.js
+++ b/app/views/RoomView/RightButtons.js
@@ -114,7 +114,7 @@ class RightButtonsContainer extends Component {
goTeamChannels = () => {
logEvent(events.ROOM_GO_TEAM_CHANNELS);
const {
- navigation, isMasterDetail, teamId
+ navigation, isMasterDetail, teamId, rid
} = this.props;
if (isMasterDetail) {
navigation.navigate('ModalStackNavigator', {
@@ -122,7 +122,7 @@ class RightButtonsContainer extends Component {
params: { teamId }
});
} else {
- navigation.navigate('TeamChannelsView', { teamId });
+ navigation.navigate('TeamChannelsView', { teamId, rid });
}
}
diff --git a/app/views/TeamChannelsView.js b/app/views/TeamChannelsView.js
index 83c206298..4fa8bb2c0 100644
--- a/app/views/TeamChannelsView.js
+++ b/app/views/TeamChannelsView.js
@@ -1,5 +1,5 @@
import React from 'react';
-import { Keyboard } from 'react-native';
+import { Keyboard, Alert } from 'react-native';
import PropTypes from 'prop-types';
import { Q } from '@nozbe/watermelondb';
import { withSafeAreaInsets } from 'react-native-safe-area-context';
@@ -28,6 +28,8 @@ import debounce from '../utils/debounce';
import { showErrorAlert } from '../utils/info';
import { goRoom } from '../utils/goRoom';
import I18n from '../i18n';
+import { withActionSheet } from '../containers/ActionSheet';
+import { deleteRoom as deleteRoomAction } from '../actions/room';
const API_FETCH_COUNT = 25;
@@ -47,12 +49,16 @@ class TeamChannelsView extends React.Component {
theme: PropTypes.string,
useRealName: PropTypes.bool,
width: PropTypes.number,
- StoreLastMessage: PropTypes.bool
+ StoreLastMessage: PropTypes.bool,
+ addTeamChannelPermission: PropTypes.array,
+ showActionSheet: PropTypes.func,
+ deleteRoom: PropTypes.func
}
constructor(props) {
super(props);
this.teamId = props.route.params?.teamId;
+ this.rid = props.route.params?.rid;
this.state = {
loading: true,
loadingMore: false,
@@ -60,9 +66,11 @@ class TeamChannelsView extends React.Component {
isSearching: false,
searchText: '',
search: [],
- end: false
+ end: false,
+ showCreate: false
};
this.loadTeam();
+ this.setHeader();
}
componentDidMount() {
@@ -70,6 +78,7 @@ class TeamChannelsView extends React.Component {
}
loadTeam = async() => {
+ const { addTeamChannelPermission } = this.props;
const db = database.active;
try {
const subCollection = db.get('subscriptions');
@@ -82,6 +91,11 @@ class TeamChannelsView extends React.Component {
if (!this.team) {
throw new Error();
}
+
+ const permissions = await RocketChat.hasPermission([addTeamChannelPermission], this.team.rid);
+ if (permissions[0]) {
+ this.setState({ showCreate: true }, () => this.setHeader());
+ }
} catch {
const { navigation } = this.props;
navigation.pop();
@@ -135,8 +149,8 @@ class TeamChannelsView extends React.Component {
}
}, 300)
- getHeader = () => {
- const { isSearching } = this.state;
+ setHeader = () => {
+ const { isSearching, showCreate, data } = this.state;
const {
navigation, isMasterDetail, insets, theme
} = this.props;
@@ -201,15 +215,11 @@ class TeamChannelsView extends React.Component {
options.headerRight = () => (
- navigation.navigate('AddChannelTeamView')} />
+ { showCreate
+ ? navigation.navigate('AddChannelTeamView', { teamId: this.teamId, teamChannels: data })} />
+ : null}
);
- return options;
- }
-
- setHeader = () => {
- const { navigation } = this.props;
- const options = this.getHeader();
navigation.setOptions(options);
}
@@ -288,6 +298,54 @@ class TeamChannelsView extends React.Component {
}
}, 1000, true);
+ options = item => ([
+ {
+ title: I18n.t('Auto-join'),
+ icon: item.t === 'p' ? 'channel-private' : 'channel-public'
+ // onPress: this.autoJoin
+ },
+ {
+ title: I18n.t('Remove_from_Team'),
+ icon: 'close',
+ danger: true,
+ onPress: this.removeFromTeam(item.id, this.teamId)
+ },
+ {
+ title: I18n.t('Delete'),
+ icon: 'delete',
+ danger: true,
+ onPress: this.delete
+ }
+ ])
+
+ delete = () => {
+ const { room } = this.state;
+ 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(room.rid, room.t)
+ }
+ ],
+ { cancelable: false }
+ );
+ }
+
+ showChannelActions = (item) => {
+ logEvent(events.ROOM_SHOW_BOX_ACTIONS);
+ const { showActionSheet } = this.props;
+ showActionSheet({ options: this.options(item) });
+ }
+
renderItem = ({ item }) => {
const {
StoreLastMessage,
@@ -303,6 +361,7 @@ class TeamChannelsView extends React.Component {
showLastMessage={StoreLastMessage}
onPress={this.onPressItem}
width={width}
+ onLongPress={this.showChannelActions}
useRealName={useRealName}
getRoomTitle={this.getRoomTitle}
getRoomAvatar={this.getRoomAvatar}
@@ -366,7 +425,12 @@ const mapStateToProps = state => ({
user: getUserSelector(state),
useRealName: state.settings.UI_Use_Real_Name,
isMasterDetail: state.app.isMasterDetail,
- StoreLastMessage: state.settings.Store_Last_Message
+ StoreLastMessage: state.settings.Store_Last_Message,
+ addTeamChannelPermission: state.permissions['add-team-channel']
});
-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)))));