<commit_before>import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import forOwn from "lodash/forOwn";
import without from "lodash/without";

import TimedUserItem from "../components/TimedUserItem.jsx";

const USER_SYMBOL_ORDER = ["~", "&", "@", "%", "+"];

class ChannelUserList extends PureComponent {
	// TODO: Change into a wrapper of <UserList />?

	shouldComponentUpdate(newProps) {
		if (newProps) {
			const { channel, channelUserLists, lastSeenUsers } = this.props;

			// Only look at changes in the relevant user list, not all of them
			var oldUserList = channelUserLists && channelUserLists[channel];
			var newUserList = newProps.channelUserLists &&
			newProps.channelUserLists[newProps.channel];

			if (oldUserList !== newUserList) {
				return true;
			}

			// Look at the usernames amongst our friends
			if (this.monitoredUserNames && this.monitoredUserNames.length) {
				const mu = this.monitoredUserNames;
				for(var i = 0; i < mu.length; i++) {
					if (
						lastSeenUsers && newProps.lastSeenUsers &&
						lastSeenUsers[mu[i]] !== newProps.lastSeenUsers[mu[i]]
					) {
						return true;
					}
				}
			}
		}

		return false;
	}

	groupUserList (userList) {
		var output = {};

		forOwn(userList, (data, username) => {
			let { symbol } = data;

			if (!output[symbol]) {
				output[symbol] = [];
			}

			output[symbol].push({ username, ...data });
		});

		forOwn(output, (list) => {
			list.sort(function(a, b) {
				if (a && b) {
					return a.username.toLowerCase()
						.localeCompare(b.username.toLowerCase());
				}
				return -1;
			});
		});

		return output;
	}

	sortedUserList (userList) {
		var output = [];
		const grouped = this.groupUserList(userList);
		const groups = Object.keys(grouped);

		if (!grouped || !groups || !groups.length) {
			return output;
		}

		// Default promoted symbols in order
		const addUsersOfSymbol = (symbol) => {
			if (grouped[symbol]) {
				output = output.concat(grouped[symbol]);
			}
		};
		USER_SYMBOL_ORDER.forEach(addUsersOfSymbol);

		// Unrecognized symbols
		const unrecognizedGroups = without(groups, ...USER_SYMBOL_ORDER.concat([""]));
		if (unrecognizedGroups && unrecognizedGroups.length) {
			unrecognizedGroups.forEach(addUsersOfSymbol);
		}

		// The rest; people without symbol
		addUsersOfSymbol("");

		return output;
	}

	render() {
		const { channel, channelUserLists, lastSeenUsers } = this.props;

		var userList = channelUserLists[channel], userListNodes = null;

		this.monitoredUserNames = [];

		if (userList) {
			const sortedList = this.sortedUserList(userList);
			userListNodes = sortedList.map((data) => {
				if (data) {
					let { displayName, username, symbol } = data;
					let userData = lastSeenUsers[username];

					if (userData) {
						this.monitoredUserNames.push(username);
					}

					return <TimedUserItem
						contextChannel={channel}
						displayName={displayName}
						skipOld={false}
						symbol={symbol}
						username={username}
						visible={true}
						{...userData}
						key={username} />;
				}
			});
		}

		return (
			<ul className="channeluserlist itemlist">{ userListNodes }</ul>
		);
	}
}

ChannelUserList.propTypes = {
	channelUserLists: PropTypes.object,
	channel: PropTypes.string,
	lastSeenUsers: PropTypes.object
};

export default connect(({
	channelUserLists,
	lastSeenUsers
}) => ({
	channelUserLists,
	lastSeenUsers
}))(ChannelUserList);<commit_msg>Fixed really old channel user list component<commit_after>import React, { PureComponent } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";
import forOwn from "lodash/forOwn";
import without from "lodash/without";

import TimedUserItem from "../components/TimedUserItem.jsx";

const USER_SYMBOL_ORDER = ["~", "&", "@", "%", "+"];

class ChannelUserList extends PureComponent {
	// TODO: Change into a wrapper of <UserList />?

	groupUserList(userList) {
		var output = {};

		forOwn(userList, (data, username) => {
			let { symbol } = data;

			if (!output[symbol]) {
				output[symbol] = [];
			}

			output[symbol].push({ username, ...data });
		});

		forOwn(output, (list) => {
			list.sort(function(a, b) {
				if (a && b) {
					return a.username.toLowerCase()
						.localeCompare(b.username.toLowerCase());
				}
				return -1;
			});
		});

		return output;
	}

	sortedUserList(userList) {
		var output = [];
		const grouped = this.groupUserList(userList);
		const groups = Object.keys(grouped);

		if (!grouped || !groups || !groups.length) {
			return output;
		}

		// Default promoted symbols in order
		const addUsersOfSymbol = (symbol) => {
			if (grouped[symbol]) {
				output = output.concat(grouped[symbol]);
			}
		};
		USER_SYMBOL_ORDER.forEach(addUsersOfSymbol);

		// Unrecognized symbols
		const unrecognizedGroups = without(groups, ...USER_SYMBOL_ORDER.concat([""]));
		if (unrecognizedGroups && unrecognizedGroups.length) {
			unrecognizedGroups.forEach(addUsersOfSymbol);
		}

		// The rest; people without symbol
		addUsersOfSymbol("");

		return output;
	}

	render() {
		const { channel, lastSeenUsers, userList } = this.props;

		var userListNodes = null;

		if (userList) {
			const sortedList = this.sortedUserList(userList);
			userListNodes = sortedList.map((data) => {
				if (data) {
					let { displayName, username, symbol } = data;
					let lastSeen = lastSeenUsers[username];

					return <TimedUserItem
						contextChannel={channel}
						displayName={displayName}
						lastSeenData={lastSeen}
						skipOld={false}
						symbol={symbol}
						username={username}
						visible={true}
						key={username} />;
				}
			});
		}

		return (
			<ul className="channeluserlist itemlist">{ userListNodes }</ul>
		);
	}
}

ChannelUserList.propTypes = {
	channel: PropTypes.string,
	lastSeenUsers: PropTypes.object,
	userList: PropTypes.object
};

const mapStateToProps = function(state, ownProps) {
	let { channel } = ownProps;
	let { channelUserLists, lastSeenUsers } = state;

	return {
		lastSeenUsers,
		userList: channelUserLists[channel]
	};
};

export default connect(mapStateToProps)(ChannelUserList);
{'fraction_non_alphanumeric': 0.13889339607334092, 'fraction_numerical': 0.00048677592081778357, 'mean_word_length': 3.545722713864307, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6708454', 'n_tokens_mistral': 2368, 'n_tokens_neox': 1948, 'n_words': 425}