From b354d25d3731b53613489d95cfa4c946cf8e0888 Mon Sep 17 00:00:00 2001 From: Alexander Smaga Date: Wed, 26 Oct 2016 15:20:45 +0300 Subject: GH-4095 Favorite/Starred Channels (#4222) --- webapp/utils/channel_utils.jsx | 135 +++++++++++++++++++++++++++++++++++++++++ webapp/utils/constants.jsx | 1 + 2 files changed, 136 insertions(+) create mode 100644 webapp/utils/channel_utils.jsx (limited to 'webapp/utils') diff --git a/webapp/utils/channel_utils.jsx b/webapp/utils/channel_utils.jsx new file mode 100644 index 000000000..119021fce --- /dev/null +++ b/webapp/utils/channel_utils.jsx @@ -0,0 +1,135 @@ + +import Constants from 'utils/constants.jsx'; +const Preferences = Constants.Preferences; + +import * as Utils from 'utils/utils.jsx'; + +import UserStore from 'stores/user_store.jsx'; +import TeamStore from 'stores/team_store.jsx'; +import PreferenceStore from 'stores/preference_store.jsx'; +import LocalizationStore from 'stores/localization_store.jsx'; + +/** + * Returns list of sorted channels grouped by type. Favorites here is considered as separated type. + * + * Example: { + * publicChannels: [...], + * privateChannels: [...], + * directChannels: [...], + * directNonTeamChannels: [...], + * favoriteChannels: [...] + * } + */ +export function buildDisplayableChannelList(persistentChannels) { + const missingDMChannels = createMissingDirectChannels(persistentChannels); + + const channels = persistentChannels.concat(missingDMChannels).map(completeDirectChannelInfo); + channels.sort(sortChannelsByDisplayName); + + const favoriteChannels = channels.filter(isFavoriteChannel); + const notFavoriteChannels = channels.filter(not(isFavoriteChannel)); + const directChannels = notFavoriteChannels.filter(andX(isDirectChannel, isDirectChannelVisible)); + + return { + favoriteChannels, + publicChannels: notFavoriteChannels.filter(isOpenChannel), + privateChannels: notFavoriteChannels.filter(isPrivateChannel), + directChannels: directChannels.filter(isConnectedToTeamMember), + directNonTeamChannels: directChannels.filter(not(isConnectedToTeamMember)) + }; +} + +export function isFavoriteChannel(channel) { + return PreferenceStore.getBool(Preferences.CATEGORY_FAVORITE_CHANNEL, channel.id); +} + +export function isOpenChannel(channel) { + return channel.type === Constants.OPEN_CHANNEL; +} + +export function isPrivateChannel(channel) { + return channel.type === Constants.PRIVATE_CHANNEL; +} + +export function isDirectChannel(channel) { + return channel.type === Constants.DM_CHANNEL; +} + +export function isDirectChannelVisible(channel) { + const channelId = Utils.getUserIdFromChannelName(channel); + + return PreferenceStore.getBool(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW, channelId); +} + +export function completeDirectChannelInfo(channel) { + if (!isDirectChannel(channel)) { + return channel; + } + + const dmChannelClone = JSON.parse(JSON.stringify(channel)); + const teammateId = Utils.getUserIdFromChannelName(channel); + + return Object.assign(dmChannelClone, { + display_name: Utils.displayUsername(teammateId), + teammate_id: teammateId, + status: UserStore.getStatus(teammateId) || 'offline' + }); +} + +export function sortChannelsByDisplayName(a, b) { + const locale = LocalizationStore.getLocale(); + + return buildDisplayNameAndTypeComparable(a).localeCompare(buildDisplayNameAndTypeComparable(b), locale, {numeric: true}); +} + +/* + * not exported helpers + */ + +function createMissingDirectChannels(channels) { + const directChannelsDisplayPreferences = PreferenceStore.getCategory(Preferences.CATEGORY_DIRECT_CHANNEL_SHOW); + + return Array. + from(directChannelsDisplayPreferences). + filter((entry) => entry[1] === 'true'). + map((entry) => entry[0]). + filter((teammateId) => !channels.some(Utils.isDirectChannelForUser.bind(null, teammateId))). + map(createFakeChannelCurried(UserStore.getCurrentId())); +} + +function createFakeChannel(userId, otherUserId) { + return { + name: Utils.getDirectChannelName(userId, otherUserId), + last_post_at: 0, + total_msg_count: 0, + type: Constants.DM_CHANNEL, + fake: true + }; +} + +function createFakeChannelCurried(userId) { + return (otherUserId) => createFakeChannel(userId, otherUserId); +} + +function isConnectedToTeamMember(channel) { + return isTeamMember(channel.teammate_id); +} + +function isTeamMember(userId) { + return TeamStore.hasActiveMemberInTeam(TeamStore.getCurrentId(), userId); +} + +function not(f) { + return (...args) => !f(...args); +} + +function andX(...fns) { + return (...args) => fns.every((f) => f(...args)); +} + +const defaultPrefix = 'D'; // fallback for future types +const typeToPrefixMap = {[Constants.OPEN_CHANNEL]: 'A', [Constants.PRIVATE_CHANNEL]: 'B', [Constants.DM_CHANNEL]: 'C'}; + +function buildDisplayNameAndTypeComparable(channel) { + return (typeToPrefixMap[channel.type] || defaultPrefix) + channel.display_name + channel.name; +} diff --git a/webapp/utils/constants.jsx b/webapp/utils/constants.jsx index 2dae06282..f87b36fc8 100644 --- a/webapp/utils/constants.jsx +++ b/webapp/utils/constants.jsx @@ -54,6 +54,7 @@ export const Preferences = { CATEGORY_THEME: 'theme', CATEGORY_FLAGGED_POST: 'flagged_post', CATEGORY_NOTIFICATIONS: 'notifications', + CATEGORY_FAVORITE_CHANNEL: 'favorite_channel', EMAIL_INTERVAL: 'email_interval' }; -- cgit v1.2.3-1-g7c22