mattermost-mobile/share_extension/ios/extension_channels.js
Chris Duarte 5c24375dae Reorganize sidebar channel items via pref (#1928)
* Reorganize sidebar channel items via pref
Add onItemPress


Add isModal prop


Add options modal for ungrouped sidebar


Filter zero item unreads from sections


Refactor unread filtering

Add SidebarSectionTypes to constants

Update mm-redux dep

Remove unused variable

* Fix lint errors. Update redux commit

* Add recent activity title

* Review feedback

* Fix issues after rebase

* Review feedback

* Change to use getAllDirectChannels

* Update redux library

* Fix lint error

* Update modal list snapshot
2018-11-09 13:06:21 -03:00

350 lines
11 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
ActivityIndicator,
Text,
View,
} from 'react-native';
import DeviceInfo from 'react-native-device-info';
import {intlShape} from 'react-intl';
import TableView from 'react-native-tableview';
import Icon from 'react-native-vector-icons/FontAwesome';
import {General} from 'mattermost-redux/constants';
import {getChannelsInTeam, getAllDirectChannels} from 'mattermost-redux/selectors/entities/channels';
import SearchBar from 'app/components/search_bar';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import ExtensionNavBar from './extension_nav_bar';
export default class ExtensionChannels extends PureComponent {
static propTypes = {
entities: PropTypes.object,
currentChannelId: PropTypes.string.isRequired,
navigator: PropTypes.object.isRequired,
onSelectChannel: PropTypes.func.isRequired,
teamId: PropTypes.string.isRequired,
theme: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
};
static contextTypes = {
intl: intlShape,
};
state = {
sections: null,
};
componentWillMount() {
this.loadChannels();
}
buildSections = (term) => {
const {channels} = this.state;
const sections = [];
const publicChannels = [];
const privateChannels = [];
const directChannels = [];
channels.forEach((channel) => {
const include = term ? channel.display_name.toLowerCase().includes(term.toLowerCase()) : true;
if (channel.display_name && include && channel.delete_at === 0) {
switch (channel.type) {
case General.OPEN_CHANNEL:
publicChannels.push(channel);
break;
case General.PRIVATE_CHANNEL:
privateChannels.push(channel);
break;
default:
directChannels.push(channel);
break;
}
}
});
if (publicChannels.length) {
sections.push({
id: 'sidebar.channels',
defaultMessage: 'PUBLIC CHANNELS',
data: publicChannels.sort(this.sortDisplayName),
});
}
if (privateChannels.length) {
sections.push({
id: 'sidebar.pg',
defaultMessage: 'PRIVATE CHANNELS',
data: privateChannels.sort(this.sortDisplayName),
});
}
if (directChannels.length) {
sections.push({
id: 'sidebar.direct',
defaultMessage: 'DIRECT MESSAGES',
data: directChannels.sort(this.sortDisplayName),
});
}
this.setState({sections});
};
cancelSearch = () => {
this.setState({term: ''});
this.buildSections();
};
goBack = () => {
this.props.navigator.pop();
};
loadChannels = async () => {
try {
const {entities, teamId} = this.props;
// get the channels for the specified team
const channelsInTeam = getChannelsInTeam({entities});
const channelIds = channelsInTeam[teamId] || [];
const direct = getAllDirectChannels({entities});
const channels = channelIds.map((id) => this.props.entities.channels.channels[id]).concat(direct);
const icons = await Promise.all([
Icon.getImageSource('globe', 16, this.props.theme.centerChannelColor),
Icon.getImageSource('lock', 16, this.props.theme.centerChannelColor),
Icon.getImageSource('user', 16, this.props.theme.centerChannelColor),
Icon.getImageSource('users', 16, this.props.theme.centerChannelColor),
]);
this.publicChannelIcon = icons[0];
this.privateChannelIcon = icons[1];
this.dmChannelIcon = icons[2];
this.gmChannelIcon = icons[3];
this.setState({
channels,
}, () => {
this.buildSections();
});
} catch (error) {
this.setState({error});
}
};
handleSearch = (term) => {
this.setState({term}, () => {
if (this.throttleTimeout) {
clearTimeout(this.throttleTimeout);
}
this.throttleTimeout = setTimeout(() => {
this.buildSections(term);
}, 300);
});
};
handleSelectChannel = (selected) => {
const {sections} = this.state;
const section = sections.find((s) => s.id === selected.detail);
if (section) {
const channel = section.data.find((c) => c.id === selected.value);
if (channel) {
this.props.onSelectChannel(channel);
this.goBack();
}
}
};
renderBody = (styles) => {
const {theme} = this.props;
const {error, sections} = this.state;
if (error) {
return (
<View style={styles.errorContainer}>
<Text style={styles.error}>
{error.message}
</Text>
</View>
);
}
if (!sections) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator/>
</View>
);
}
return (
<TableView
tableViewStyle={TableView.Consts.Style.Plain}
tableViewCellStyle={TableView.Consts.CellStyle.Default}
separatorColor={changeOpacity(theme.centerChannelColor, 0.5)}
tintColor={theme.linkColor}
detailFontSize={16}
detailTextColor={theme.centerChannelColor}
headerFontSize={15}
headerTextColor={changeOpacity(theme.centerChannelColor, 0.6)}
style={styles.flex}
>
<TableView.Header>
<TableView.Cell>{this.renderSearchBar(styles)}</TableView.Cell>
</TableView.Header>
{this.renderSections()}
</TableView>
);
};
renderSearchBar = (styles) => {
const {formatMessage} = this.context.intl;
const {theme} = this.props;
return (
<View style={styles.searchContainer}>
<SearchBar
ref='search_bar'
placeholder={formatMessage({id: 'search_bar.search', defaultMessage: 'Search'})}
cancelTitle={formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'})}
backgroundColor='transparent'
inputHeight={33}
inputStyle={styles.searchBarInput}
placeholderTextColor={changeOpacity(theme.centerChannelColor, 0.5)}
tintColorSearch={changeOpacity(theme.centerChannelColor, 0.5)}
tintColorDelete={changeOpacity(theme.centerChannelColor, 0.3)}
titleCancelColor={theme.linkColor}
onChangeText={this.handleSearch}
onCancelButtonPress={this.cancelSearch}
autoCapitalize='none'
value={this.state.term}
/>
</View>
);
};
renderSections = () => {
const {formatMessage} = this.context.intl;
const {currentChannelId} = this.props;
const {sections} = this.state;
return sections.map((s) => {
const items = s.data.map((c) => {
let icon;
switch (c.type) {
case General.OPEN_CHANNEL:
icon = this.publicChannelIcon.uri;
break;
case General.PRIVATE_CHANNEL:
icon = this.privateChannelIcon.uri;
break;
case General.DM_CHANNEL:
icon = this.dmChannelIcon.uri;
break;
case General.GM_CHANNEL:
icon = this.gmChannelIcon.uri;
break;
}
return (
<TableView.Item
key={c.id}
value={c.id}
detail={s.id}
selected={c.id === currentChannelId}
onPress={this.handleSelectChannel}
image={icon}
>
{c.display_name}
</TableView.Item>
);
});
return (
<TableView.Section
key={s.id}
label={formatMessage({id: s.id, defaultMessage: s.defaultMessage})}
headerHeight={30}
>
{items}
</TableView.Section>
);
});
};
sortDisplayName = (a, b) => {
const locale = DeviceInfo.getDeviceLocale().split('-')[0];
return a.display_name.localeCompare(b.display_name, locale, {numeric: true});
};
render() {
const {theme, title} = this.props;
const styles = getStyleSheet(theme);
return (
<View style={styles.flex}>
<ExtensionNavBar
backButton={true}
onLeftButtonPress={this.goBack}
title={title}
theme={theme}
/>
{this.renderBody(styles)}
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
flex: {
flex: 1,
},
separatorContainer: {
paddingLeft: 35,
},
separator: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
height: 1,
},
loadingContainer: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
},
searchContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
paddingBottom: 2,
paddingHorizontal: 5,
},
searchBarInput: {
backgroundColor: '#fff',
color: theme.centerChannelColor,
fontSize: 15,
},
titleContainer: {
height: 30,
},
title: {
color: changeOpacity(theme.centerChannelColor, 0.6),
fontSize: 15,
lineHeight: 30,
paddingHorizontal: 15,
},
errorContainer: {
alignItems: 'center',
flex: 1,
justifyContent: 'center',
paddingHorizontal: 15,
},
error: {
color: theme.errorTextColor,
fontSize: 14,
},
};
});