import { h, render, Component } from 'preact'; import PropTypes from 'prop-types'; import { setupPusher } from './pusher'; /* global userData */ class UnopenedChannelNotice extends Component { propTypes = { pusherKey: PropTypes.Object, }; static defaultProps = { pusherKey: undefined, }; constructor(props) { super(props); this.state = { visible: false, unopenedChannels: [], }; } componentDidMount() { const { pusherKey } = this.props; setupPusher(pusherKey, { channelId: `private-message-notifications-${window.currentUser.id}`, messageCreated: this.receiveNewMessage, messageDeleted: this.removeMessage, messageEdited: this.updateMessage, mentioned: this.mentionedMessage, messageOpened: this.messageOpened, }); this.fetchUnopenedChannel(this.updateMessageNotification); document.getElementById('connect-link').onclick = () => { // Hack, should probably be its own component in future document.getElementById('connect-number').classList.add('hidden'); this.setState({ visible: false }); }; } updateMessageNotification = (unopenedChannels) => { const number = document.getElementById('connect-number'); this.setState({ unopenedChannels }); if (unopenedChannels.length > 0) { if (unopenedChannels[0].adjusted_slug === `@${userData().username}`) { return; } number.classList.remove('hidden'); number.innerHTML = unopenedChannels.length; document.getElementById( 'connect-link', ).href = `/connect/${unopenedChannels[0].adjusted_slug}`; InstantClick.preload( document.getElementById('connect-link').href, 'force', ); } else { number.classList.add('hidden'); } }; removeMessage = () => {}; updateMessage = () => {}; mentionedMessage = (e) => { if (window.location.pathname.startsWith('/connect')) { return; } this.setState((prevState) => ({ unopenedChannels: prevState.unopenedChannels.map((unopenedChannel) => unopenedChannel.adjusted_slug === e.chat_channel_adjusted_slug ? { ...unopenedChannel, request_type: 'mentioned' } : unopenedChannel, ), visible: true, })); this.hideNotice(); }; messageOpened = (e) => { const { unopenedChannels } = this.state; if ( !window.location.pathname.startsWith('/connect') || !window.location.pathname.includes(e.adjusted_slug) ) return; this.updateMessageNotification( unopenedChannels.filter( (unopenedChannel) => unopenedChannel.adjusted_slug !== e.adjusted_slug, ), ); }; receiveNewMessage = (e) => { if ( (window.location.pathname.startsWith('/connect') && e.user_id === window.currentUser.id && e.channel_type !== 'direct') || window.location.pathname.includes(e.chat_channel_adjusted_slug) ) { return; } const { unopenedChannels } = this.state; const newObj = { adjusted_slug: e.chat_channel_adjusted_slug }; const ifMessageExist = unopenedChannels.some( (channel) => channel.adjusted_slug === newObj.adjusted_slug, ); if ( !ifMessageExist && newObj.adjusted_slug !== `@${window.currentUser.username}` ) { unopenedChannels.push(newObj); } if (ifMessageExist) { const index = unopenedChannels.findIndex( (channel) => channel.adjusted_slug === newObj.adjusted_slug, ); unopenedChannels[index].notified = false; } if (!window.location.pathname.startsWith('/connect')) { this.setState({ visible: unopenedChannels.length > 0 && e.user_id !== window.currentUser.id && e.channel_type === 'direct', }); } this.updateMessageNotification(unopenedChannels); this.hideNotice(); }; handleClick = () => { this.hideNotice(); }; hideNotice = () => { setTimeout(() => { this.setState((prevState) => ({ unopenedChannels: prevState.unopenedChannels.map((unopenedChannel) => !unopenedChannel.notified ? { ...unopenedChannel, notified: true } : unopenedChannel, ), visible: false, })); }, 7500); }; fetchUnopenedChannel = (successCb) => { fetch('/chat_channels?state=unopened', { Accept: 'application/json', 'Content-Type': 'application/json', credentials: 'same-origin', }) .then((response) => response.json()) .then(successCb); }; render() { const { visible, unopenedChannels } = this.state; if (visible && unopenedChannels.some((channel) => !channel.notified)) { const message = unopenedChannels.map((channel) => { if (channel.notified) return null; return (