Make code work more properly and bind pusher states (#440)

This commit is contained in:
Ben Halpern 2018-06-14 12:50:50 -04:00 committed by GitHub
parent cc45ec8ec6
commit 115ed2e7f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 73 deletions

View file

@ -329,6 +329,17 @@
overflow-x: hidden;
}
.chatchannels__channelslistheader{
background: lighten($purple, 5%);
border: 1px solid darken($purple, 20%);
font-size: 12px;
color: $dark-purple;
box-sizing: border-box;
padding: 5px;
width: 98%;
border-radius: 3px;
}
.chatchannels__channelslistfooter{
font-size: 12px;
color: $medium-gray;

View file

@ -2,11 +2,11 @@ import { h } from 'preact';
import PropTypes from 'prop-types';
import ConfigImage from 'images/three-dots.svg';
const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel, expanded }) => {
const channels = chatChannels.map(channel => {
const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel, expanded, filterQuery, channelsLoaded }) => {
const channels = chatChannels.map((channel, index) => {
const isActive = parseInt(activeChannelId, 10) === channel.id
const lastOpened = channel.last_opened_at ? channel.last_opened_at : channel.channel_users[window.currentUser.username].last_opened_at
const isUnopened = new Date(channel.last_message_at) > new Date(lastOpened);
const isUnopened = (new Date(channel.last_message_at) > new Date(lastOpened)) && channel.messages_count > 0;
const otherClassname =
isActive
@ -50,7 +50,6 @@ const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel, expanded
content = name
}
}
return (
<button
key={channel.id}
@ -73,9 +72,19 @@ const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel, expanded
if (channels.length === 30) {
channelsListFooter = <div className="chatchannels__channelslistfooter">You may connect devs you mutually follow. Use the filter to discover all your channels.</div>
}
let topNotice = ''
if (expanded &&
filterQuery.length === 0 &&
channelsLoaded &&
(channels.length === 0 || channels[0].messages_count === 0) ) {
topNotice = <div className="chatchannels__channelslistheader">
👋 Welcome to <b>DEV Connect</b>! You can may chat with anyone you mutually follow.
</div>
}
return (
<div className="chatchannels">
<div className="chatchannels__channelslist">
{topNotice}
{channels}
{channelsListFooter}
</div>

View file

@ -25,6 +25,8 @@ export default class Chat extends Component {
scrolled: false,
showAlert: false,
chatChannels,
filterQuery: '',
channelsLoaded: false,
activeChannelId: chatOptions.activeChannelId,
activeChannel: null,
showChannelsList: chatOptions.showChannelsList,
@ -33,6 +35,7 @@ export default class Chat extends Component {
activeContent: null,
expanded: window.innerWidth > 600,
isMobileDevice: typeof window.orientation !== "undefined",
subscribedPusherChannels: []
};
}
@ -42,22 +45,11 @@ export default class Chat extends Component {
this.setupChannel(channel.id);
}
if (channel.channel_type === "open") {
setupPusher(this.props.pusherKey, {
channelId: `open-channel-${channel.id}`,
messageCreated: this.receiveNewMessage,
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
liveCoding: null
});
this.subscribePusher(`open-channel-${channel.id}`)
}
});
setupObserver(this.observerCallback);
setupPusher(this.props.pusherKey, {
channelId: `private-message-notifications-${window.currentUser.id}`,
messageCreated: this.receiveNewMessage,
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
});
this.subscribePusher(`private-message-notifications-${window.currentUser.id}`)
if (this.state.activeChannelId) {
sendOpen(
this.state.activeChannelId,
@ -83,22 +75,57 @@ export default class Chat extends Component {
}
liveCoding = e => {
if (this.state.activeContent === {type_of: "code_editor"}) {
return
if (this.state.activeContent != {type_of: "code_editor"}) {
this.setState({activeContent: {type_of: "code_editor"}})
}
if (document.querySelector(".CodeMirror")) {
let cm = document.querySelector(".CodeMirror").CodeMirror
if (cm && e.context === 'initializing-live-code-channel') {
window.pusher.channel(e.channel).trigger('client-livecode', {
value: cm.getValue(),
cursorPos: cm.getCursor(),
});
} else if (cm && e.keyPressed === true || e.value.length > 0) {
const cursorCoords = e.cursorPos
const cursorElement = document.createElement('span');
cursorElement.classList.add("cursorelement")
cursorElement.style.height = `${(cursorCoords.bottom - cursorCoords.top)}px`;
cm.setValue(e.value);
cm.setBookmark(e.cursorPos, { widget: cursorElement });
}
}
this.setState({activeContent: {type_of: "code_editor"}})
}
filterForActiveChannel = (channels, id) => {
return channels.filter(channel => channel.id === parseInt(id))[0]
}
subscribePusher = channelName => {
if (this.state.subscribedPusherChannels.includes(channelName)){
return
} else {
setupPusher(this.props.pusherKey, {
channelId: channelName,
messageCreated: this.receiveNewMessage,
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
channelError: this.channelError,
liveCoding: this.liveCoding,
});
let subscriptions = this.state.subscribedPusherChannels;
subscriptions.push(channelName);
this.setState({subscribedPusherChannels:subscriptions})
}
}
loadChannels = (channels, query) => {
if (this.state.activeChannelId && query.length === 0) {
this.setupChannel(this.state.activeChannelId);
this.setState({
chatChannels: channels,
scrolled: false,
channelsLoaded: true,
activeChannel: this.filterForActiveChannel(channels, this.state.activeChannelId)
});
} if (this.state.activeChannelId) {
@ -106,10 +133,13 @@ export default class Chat extends Component {
this.setState({
scrolled: false,
chatChannels: channels,
channelsLoaded: true,
filterQuery: query
});
} else {
} else if (channels.length > 0) {
this.setState({
chatChannels: channels,
channelsLoaded: true,
scrolled: false,
});
const channel = channels[0]
@ -117,19 +147,15 @@ export default class Chat extends Component {
'@'+channel.slug.replace(`${window.currentUser.username}/`, '').replace(`/${window.currentUser.username}`, '') :
channel.slug
this.triggerSwitchChannel(channel.id, channelSlug);
} else {
this.setState({channelsLoaded: true})
}
channels.forEach((channel, index) => {
if ( index < 3 ) {
this.setupChannel(channel.id);
}
if (channel.channel_type === "invite_only"){
setupPusher(this.props.pusherKey, {
channelId: `presence-channel-${channel.id}`,
messageCreated: this.receiveNewMessage,
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
liveCoding: this.liveCoding
});
this.subscribePusher(`presence-channel-${channel.id}`)
}
});
}
@ -139,13 +165,7 @@ export default class Chat extends Component {
this.state.messages[channelId][0].reception_method === 'pushed'){
getAllMessages(channelId, this.receiveAllMessages);
}
setupPusher(this.props.pusherKey, {
channelId: `presence-channel-${channelId}`,
messageCreated: this.receiveNewMessage,
channelCleared: this.clearChannel,
redactUserMessages: this.redactUserMessages,
liveCoding: this.liveCoding
});
this.subscribePusher(`presence-channel-${channelId}`)
};
@ -159,6 +179,12 @@ export default class Chat extends Component {
});
};
channelError = error => {
this.setState({
subscribedPusherChannels: [],
})
}
receiveAllMessages = res => {
const { chatChannelId, messages } = res;
const newMessages = { ...this.state.messages, [chatChannelId]: messages };
@ -416,6 +442,8 @@ export default class Chat extends Component {
activeChannelId={this.state.activeChannelId}
chatChannels={this.state.chatChannels}
handleSwitchChannel={this.handleSwitchChannel}
channelsLoaded={this.state.channelsLoaded}
filterQuery={this.state.filterQuery}
expanded={this.state.expanded}
/>
{notificationsState}

View file

@ -1,6 +1,5 @@
import { h, Component } from 'preact';
import PropTypes from 'prop-types';
import setupPusher from '../src/utils/pusher';
import CodeMirror from 'codemirror';
import 'codemirror/mode/javascript/javascript';
import 'codemirror/mode/jsx/jsx';
@ -10,10 +9,6 @@ export default class CodeEditor extends Component {
componentDidMount() {
const editor = document.getElementById("codeeditor");
const channel = setupPusher(this.props.pusherKey, {
channelId: `presence-channel-${this.props.activeChannelId}`,
liveCoding: this.liveCoding,
});
const myCodeMirror = CodeMirror(editor, {
mode: "javascript",
theme: "material",
@ -21,9 +16,10 @@ export default class CodeEditor extends Component {
});
myCodeMirror.setSize("100%", "100%");
//Initial trigger:
const channel = window.pusher.channel(`presence-channel-${this.props.activeChannelId}`)
channel.trigger('client-livecode', {
value: myCodeMirror.getValue(),
cursorPos: myCodeMirror.getCursor(),
context: 'initializing-live-code-channel',
channel: `presence-channel-${this.props.activeChannelId}`
});
//Coding trigger:
myCodeMirror.on('keyup', cm => {
@ -38,24 +34,6 @@ export default class CodeEditor extends Component {
shouldComponentUpdate() {
return false;
}
liveCoding = e => {
if (e.keyPressed === true || e.value.length > 0) {
let cm = document.querySelector(".CodeMirror").CodeMirror
const cursorCoords = e.cursorPos
const cursorElement = document.createElement('span');
cursorElement.classList.add("cursorelement")
cursorElement.style.height = `${(cursorCoords.bottom - cursorCoords.top)}px`;
cm.setValue(e.value);
cm.setBookmark(e.cursorPos, { widget: cursorElement });
} else {
let cm = document.querySelector(".CodeMirror").CodeMirror
channel.trigger('client-livecode', {
value: cm.getValue(),
cursorPos: cm.getCursor(),
});
}
}
render() {

View file

@ -1,21 +1,29 @@
import Pusher from 'pusher-js';
export default function setupPusher(key, callbackObjects) {
const pusher = new Pusher(key, {
authEndpoint: '/pusher/auth',
auth: {
headers: {
'X-CSRF-Token': window.csrfToken
}
},
cluster: 'us2',
encrypted: true,
});
pusher.unsubscribe(callbackObjects.channelId.toString());
let pusher;
if (window.pusher) {
pusher = window.pusher
} else {
pusher = new Pusher(key, {
authEndpoint: '/pusher/auth',
auth: {
headers: {
'X-CSRF-Token': window.csrfToken
}
},
cluster: 'us2',
encrypted: true,
});
window.pusher = pusher;
}
const channel = pusher.subscribe(callbackObjects.channelId.toString());
channel.bind('message-created', callbackObjects.messageCreated);
channel.bind('channel-cleared', callbackObjects.channelCleared);
channel.bind('user-banned', callbackObjects.redactUserMessages);
channel.bind('client-livecode', callbackObjects.liveCoding);
// channel.bind('pusher:subscription_succeeded', callbackObjects.channelSubscribed);
channel.bind('pusher:subscription_error', callbackObjects.channelError);
return channel;
}
}

View file

@ -12,7 +12,8 @@ class ChatChannel < ApplicationRecord
algoliasearch index_name: "SecuredChatChannel_#{Rails.env}" do
attribute :id, :viewable_by, :slug, :channel_type,
:channel_name, :channel_users, :last_message_at, :status
:channel_name, :channel_users, :last_message_at, :status,
:messages_count
searchableAttributes [:channel_name,:channel_slug]
attributesForFaceting ["filterOnly(viewable_by)","filterOnly(status)"]
ranking ["desc(last_message_at)"]
@ -57,6 +58,7 @@ class ChatChannel < ApplicationRecord
status: "active",
)
channel.add_users(users)
channel.index!
end
channel
end
@ -93,6 +95,10 @@ class ChatChannel < ApplicationRecord
chat_channel_memberships.pluck(:user_id)
end
def messages_count
messages.size
end
def channel_users
pics_obj = {}
chat_channel_memberships.includes(:user).each do |m|