diff --git a/app/lib/methods/subscriptions/rooms.ts b/app/lib/methods/subscriptions/rooms.ts index b18122566..c62dcbe76 100644 --- a/app/lib/methods/subscriptions/rooms.ts +++ b/app/lib/methods/subscriptions/rooms.ts @@ -410,7 +410,6 @@ export default function subscribeRooms() { log(e); } } - if (/video-conference/.test(ev)) { const [action, params] = ddpMessage.fields.args; store.dispatch(handleVideoConfIncomingWebsocketMessages({ action, params })); diff --git a/app/stacks/InsideStack.tsx b/app/stacks/InsideStack.tsx index 54162d730..b4c1d677e 100644 --- a/app/stacks/InsideStack.tsx +++ b/app/stacks/InsideStack.tsx @@ -128,11 +128,7 @@ const ChatsStackNavigator = () => { - + {/* @ts-ignore */} {/* @ts-ignore */} diff --git a/app/stacks/MasterDetailStack/index.tsx b/app/stacks/MasterDetailStack/index.tsx index 5c9235120..285e145e2 100644 --- a/app/stacks/MasterDetailStack/index.tsx +++ b/app/stacks/MasterDetailStack/index.tsx @@ -130,12 +130,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => { {/* @ts-ignore */} - + diff --git a/app/stacks/types.ts b/app/stacks/types.ts index 1002c75ec..0f73fabed 100644 --- a/app/stacks/types.ts +++ b/app/stacks/types.ts @@ -10,7 +10,6 @@ import { IMessage, TAnyMessageModel, TMessageModel } from '../definitions/IMessa import { TServerModel } from '../definitions/IServer'; import { ISubscription, SubscriptionType, TSubscriptionModel } from '../definitions/ISubscription'; import { TChangeAvatarViewContext } from '../definitions/TChangeAvatarViewContext'; -import { IItem } from '../views/TeamChannelsView'; import { MasterDetailInsideStackParamList, ModalStackParamList } from './MasterDetailStack/types'; import { TNavigation } from './stackType'; @@ -154,12 +153,10 @@ export type ChatsStackParamList = { teamId?: string; }; AddChannelTeamView: { - teamId?: string; - teamChannels: IItem[]; + teamId: string; }; AddExistingChannelView: { - teamId?: string; - teamChannels: IItem[]; + teamId: string; }; MarkdownTableView: { renderRows: (drawExtraBorders?: boolean) => JSX.Element; diff --git a/app/views/AddChannelTeamView.tsx b/app/views/AddChannelTeamView.tsx index 7b924b59e..e2da06e98 100644 --- a/app/views/AddChannelTeamView.tsx +++ b/app/views/AddChannelTeamView.tsx @@ -40,7 +40,9 @@ const setHeader = ({ const AddChannelTeamView = () => { const navigation = useNavigation(); const isMasterDetail = useSelector((state: IApplicationState) => state.app.isMasterDetail); - const { teamChannels, teamId } = useRoute().params; + const { + params: { teamId } + } = useRoute(); useEffect(() => { setHeader({ navigation, isMasterDetail }); @@ -70,7 +72,7 @@ const AddChannelTeamView = () => { navigation.navigate('AddExistingChannelView', { teamId, teamChannels })} + onPress={() => navigation.navigate('AddExistingChannelView', { teamId })} testID='add-channel-team-view-add-existing' left={() => } right={() => } diff --git a/app/views/AddExistingChannelView.tsx b/app/views/AddExistingChannelView.tsx deleted file mode 100644 index cefa750ab..000000000 --- a/app/views/AddExistingChannelView.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import React from 'react'; -import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; -import { RouteProp } from '@react-navigation/native'; -import { FlatList } from 'react-native'; -import { connect } from 'react-redux'; -import { Q } from '@nozbe/watermelondb'; - -import * as List from '../containers/List'; -import database from '../lib/database'; -import I18n from '../i18n'; -import log, { events, logEvent } from '../lib/methods/helpers/log'; -import SearchBox from '../containers/SearchBox'; -import * as HeaderButton from '../containers/HeaderButton'; -import StatusBar from '../containers/StatusBar'; -import { themes } from '../lib/constants'; -import { TSupportedThemes, withTheme } from '../theme'; -import SafeAreaView from '../containers/SafeAreaView'; -import { sendLoadingEvent } from '../containers/Loading'; -import { animateNextTransition } from '../lib/methods/helpers/layoutAnimation'; -import { showErrorAlert } from '../lib/methods/helpers/info'; -import { ChatsStackParamList } from '../stacks/types'; -import { TSubscriptionModel, SubscriptionType, IApplicationState } from '../definitions'; -import { getRoomTitle, hasPermission, debounce } from '../lib/methods/helpers'; -import { Services } from '../lib/services'; - -interface IAddExistingChannelViewState { - search: TSubscriptionModel[]; - channels: TSubscriptionModel[]; - selected: string[]; -} - -interface IAddExistingChannelViewProps { - navigation: StackNavigationProp; - route: RouteProp; - theme?: TSupportedThemes; - isMasterDetail: boolean; - addTeamChannelPermission?: string[]; -} - -const QUERY_SIZE = 50; - -class AddExistingChannelView extends React.Component { - private teamId: string; - - constructor(props: IAddExistingChannelViewProps) { - super(props); - this.query(); - this.teamId = props.route?.params?.teamId ?? ''; - this.state = { - search: [], - channels: [], - selected: [] - }; - this.setHeader(); - } - - setHeader = () => { - const { navigation, isMasterDetail } = this.props; - const { selected } = this.state; - - const options: StackNavigationOptions = { - headerTitle: I18n.t('Add_Existing_Channel') - }; - - if (isMasterDetail) { - options.headerLeft = () => ; - } - - options.headerRight = () => - selected.length > 0 && ( - - - - ); - - navigation.setOptions(options); - }; - - query = async (stringToSearch = '') => { - try { - const { addTeamChannelPermission } = this.props; - const db = database.active; - const channels = await db - .get('subscriptions') - .query( - Q.where('team_id', ''), - Q.where('t', Q.oneOf(['c', 'p'])), - Q.where('name', Q.like(`%${stringToSearch}%`)), - Q.take(QUERY_SIZE), - Q.sortBy('room_updated_at', Q.desc) - ) - .fetch(); - - const asyncFilter = async (channelsArray: TSubscriptionModel[]) => { - const results = await Promise.all( - channelsArray.map(async channel => { - if (channel.prid) { - return false; - } - const permissions = await hasPermission([addTeamChannelPermission], channel.rid); - if (!permissions[0]) { - return false; - } - return true; - }) - ); - - return channelsArray.filter((_v: any, index: number) => results[index]); - }; - const channelFiltered = await asyncFilter(channels); - this.setState({ channels: channelFiltered }); - } catch (e) { - log(e); - } - }; - - onSearchChangeText = debounce((text: string) => { - this.query(text); - }, 300); - - dismiss = () => { - const { navigation } = this.props; - return navigation.pop(); - }; - - submit = async () => { - const { selected } = this.state; - const { navigation } = this.props; - - sendLoadingEvent({ visible: true }); - try { - logEvent(events.CT_ADD_ROOM_TO_TEAM); - const result = await Services.addRoomsToTeam({ rooms: selected, teamId: this.teamId }); - if (result.success) { - sendLoadingEvent({ visible: false }); - // Expect that after you add an existing channel to a team, the user should move back to the team - navigation.navigate('RoomView'); - } - } catch (e: any) { - logEvent(events.CT_ADD_ROOM_TO_TEAM_F); - showErrorAlert(I18n.t(e.data.error), I18n.t('Add_Existing_Channel'), () => {}); - sendLoadingEvent({ visible: false }); - } - }; - - renderHeader = () => ( - this.onSearchChangeText(text)} testID='add-existing-channel-view-search' /> - ); - - isChecked = (rid: string) => { - const { selected } = this.state; - return selected.includes(rid); - }; - - toggleChannel = (rid: string) => { - const { selected } = this.state; - - animateNextTransition(); - if (!this.isChecked(rid)) { - logEvent(events.AEC_ADD_CHANNEL); - this.setState({ selected: [...selected, rid] }, () => this.setHeader()); - } else { - logEvent(events.AEC_REMOVE_CHANNEL); - const filterSelected = selected.filter(el => el !== rid); - this.setState({ selected: filterSelected }, () => this.setHeader()); - } - }; - - renderItem = ({ item }: { item: TSubscriptionModel }) => { - const isChecked = this.isChecked(item.rid); - // TODO: reuse logic inside RoomTypeIcon - const icon = item.t === SubscriptionType.DIRECT && !item?.teamId ? 'channel-private' : 'channel-public'; - return ( - this.toggleChannel(item.rid)} - testID={`add-existing-channel-view-item-${item.name}`} - left={() => } - right={() => (isChecked ? : null)} - /> - ); - }; - - 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() { - return ( - - - {this.renderList()} - - ); - } -} - -const mapStateToProps = (state: IApplicationState) => ({ - isMasterDetail: state.app.isMasterDetail, - addTeamChannelPermission: state.permissions['add-team-channel'] -}); - -export default connect(mapStateToProps)(withTheme(AddExistingChannelView)); diff --git a/app/views/AddExistingChannelView/index.tsx b/app/views/AddExistingChannelView/index.tsx new file mode 100644 index 000000000..c42751f61 --- /dev/null +++ b/app/views/AddExistingChannelView/index.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useLayoutEffect, useState } from 'react'; +import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack'; +import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; +import { FlatList } from 'react-native'; +import { Q } from '@nozbe/watermelondb'; + +import * as List from '../../containers/List'; +import database from '../../lib/database'; +import I18n from '../../i18n'; +import log, { events, logEvent } from '../../lib/methods/helpers/log'; +import SearchBox from '../../containers/SearchBox'; +import * as HeaderButton from '../../containers/HeaderButton'; +import StatusBar from '../../containers/StatusBar'; +import { useTheme } from '../../theme'; +import SafeAreaView from '../../containers/SafeAreaView'; +import { sendLoadingEvent } from '../../containers/Loading'; +import { animateNextTransition } from '../../lib/methods/helpers/layoutAnimation'; +import { showErrorAlert } from '../../lib/methods/helpers/info'; +import { ChatsStackParamList } from '../../stacks/types'; +import { TSubscriptionModel, SubscriptionType } from '../../definitions'; +import { getRoomTitle, hasPermission, useDebounce } from '../../lib/methods/helpers'; +import { Services } from '../../lib/services'; +import { useAppSelector } from '../../lib/hooks'; + +type TNavigation = StackNavigationProp; +type TRoute = RouteProp; + +const QUERY_SIZE = 50; + +const AddExistingChannelView = () => { + const [channels, setChannels] = useState([]); + const [selected, setSelected] = useState([]); + + const { colors } = useTheme(); + + const navigation = useNavigation(); + const { + params: { teamId } + } = useRoute(); + + const { addTeamChannelPermission, isMasterDetail } = useAppSelector(state => ({ + isMasterDetail: state.app.isMasterDetail, + addTeamChannelPermission: state.permissions['add-team-channel'] + })); + + useLayoutEffect(() => { + setHeader(); + }, [selected.length]); + + useEffect(() => { + query(); + }, []); + + const setHeader = () => { + const options: StackNavigationOptions = { + headerTitle: I18n.t('Add_Existing_Channel') + }; + + if (isMasterDetail) { + options.headerLeft = () => ; + } + + options.headerRight = () => + selected.length > 0 && ( + + + + ); + + navigation.setOptions(options); + }; + + const query = async (stringToSearch = '') => { + try { + const db = database.active; + const channels = await db + .get('subscriptions') + .query( + Q.where('team_id', ''), + Q.where('t', Q.oneOf(['c', 'p'])), + Q.where('name', Q.like(`%${stringToSearch}%`)), + Q.take(QUERY_SIZE), + Q.sortBy('room_updated_at', Q.desc) + ) + .fetch(); + + const asyncFilter = async (channelsArray: TSubscriptionModel[]) => { + const results = await Promise.all( + channelsArray.map(async channel => { + if (channel.prid) { + return false; + } + const permissions = await hasPermission([addTeamChannelPermission], channel.rid); + if (!permissions[0]) { + return false; + } + return true; + }) + ); + + return channelsArray.filter((_v: any, index: number) => results[index]); + }; + const channelFiltered = await asyncFilter(channels); + setChannels(channelFiltered); + } catch (e) { + log(e); + } + }; + + const onSearchChangeText = useDebounce((text: string) => { + query(text); + }, 300); + + const isChecked = (rid: string) => selected.includes(rid); + + const toggleChannel = (rid: string) => { + animateNextTransition(); + if (!isChecked(rid)) { + logEvent(events.AEC_ADD_CHANNEL); + setSelected([...selected, rid]); + } else { + logEvent(events.AEC_REMOVE_CHANNEL); + const filterSelected = selected.filter(el => el !== rid); + setSelected(filterSelected); + } + }; + + const submit = async () => { + sendLoadingEvent({ visible: true }); + try { + logEvent(events.CT_ADD_ROOM_TO_TEAM); + const result = await Services.addRoomsToTeam({ rooms: selected, teamId }); + if (result.success) { + sendLoadingEvent({ visible: false }); + // Expect that after you add an existing channel to a team, the user should move back to the team + navigation.navigate('RoomView'); + } + } catch (e: any) { + logEvent(events.CT_ADD_ROOM_TO_TEAM_F); + showErrorAlert(I18n.t(e.data.error), I18n.t('Add_Existing_Channel'), () => {}); + sendLoadingEvent({ visible: false }); + } + }; + + return ( + + + item.id} + ListHeaderComponent={ + onSearchChangeText(text)} testID='add-existing-channel-view-search' /> + } + renderItem={({ item }: { item: TSubscriptionModel }) => { + // TODO: reuse logic inside RoomTypeIcon + const icon = item.t === SubscriptionType.GROUP && !item?.teamId ? 'channel-private' : 'channel-public'; + return ( + toggleChannel(item.rid)} + testID={`add-existing-channel-view-item-${item.name}`} + left={() => } + right={() => (isChecked(item.rid) ? : null)} + /> + ); + }} + ItemSeparatorComponent={List.Separator} + contentContainerStyle={{ backgroundColor: colors.backgroundColor }} + keyboardShouldPersistTaps='always' + /> + + ); +}; + +export default AddExistingChannelView; diff --git a/app/views/TeamChannelsView.tsx b/app/views/TeamChannelsView.tsx index bce566cb6..243978b90 100644 --- a/app/views/TeamChannelsView.tsx +++ b/app/views/TeamChannelsView.tsx @@ -186,7 +186,7 @@ class TeamChannelsView extends React.Component { - const { isSearching, showCreate, data } = this.state; + const { isSearching, showCreate } = this.state; const { navigation, isMasterDetail, theme } = this.props; const { team } = this; @@ -234,7 +234,7 @@ class TeamChannelsView extends React.Component navigation.navigate('AddChannelTeamView', { teamId: this.teamId, teamChannels: data })} + onPress={() => navigation.navigate('AddChannelTeamView', { teamId: this.teamId })} /> ) : null}