Update chat and modify article score calc indexing (#395)
This commit is contained in:
parent
851bb4e336
commit
ab85216621
21 changed files with 217 additions and 63 deletions
|
|
@ -23,7 +23,7 @@ function markNotificationsAsRead() {
|
|||
xmlhttp.setRequestHeader('X-CSRF-Token', csrfToken);
|
||||
xmlhttp.send();
|
||||
}
|
||||
}, 120);
|
||||
}, 250);
|
||||
}
|
||||
|
||||
function fetchNotificationsCount() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,28 @@
|
|||
@import 'variables';
|
||||
|
||||
// High level class
|
||||
.chat-page-header{
|
||||
background: $green;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
margin-top: -25px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 5px;
|
||||
font-size:0.8em;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chat {
|
||||
display: flex;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.chat__channels {
|
||||
width: 160px;
|
||||
width: 80px;
|
||||
height: inherit;
|
||||
@media screen and ( min-width: 550px ){
|
||||
width: 160px;
|
||||
}
|
||||
}
|
||||
|
||||
.chat__channels--hidden {
|
||||
|
|
@ -89,9 +103,10 @@
|
|||
width: inherit;
|
||||
}
|
||||
.chatchanneltabbutton{
|
||||
width: 92%;
|
||||
width: 93%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
padding:0;
|
||||
&:hover {
|
||||
.chatchanneltab--inactive {
|
||||
border: 1px solid $outline-color;
|
||||
|
|
@ -101,19 +116,22 @@
|
|||
}
|
||||
.chatchanneltab {
|
||||
display:inline-block;
|
||||
width: 95%;
|
||||
width: 90%;
|
||||
margin-bottom: 5px;
|
||||
border-left: 8px solid transparent;
|
||||
background: inherit;
|
||||
text-align: start;
|
||||
font-size: 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
padding: 8px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;}
|
||||
text-overflow: ellipsis;
|
||||
@media screen and ( min-width: 550px ){
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
.chatchanneltab--active {
|
||||
background: white;
|
||||
|
|
@ -125,9 +143,9 @@
|
|||
display: inline-block;
|
||||
background: white;
|
||||
border: 2px solid $light-medium-gray;
|
||||
padding: 5px;
|
||||
padding: 0.3em;
|
||||
border-radius: 100px;
|
||||
vertical-align: -2px;
|
||||
vertical-align: -0.05em;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
|
|
@ -136,18 +154,6 @@
|
|||
border: 2px solid $black;
|
||||
}
|
||||
|
||||
@keyframes example {
|
||||
// animation-name: example;
|
||||
// animation-duration: 4s;
|
||||
// animation-iteration-count: infinite;
|
||||
from {
|
||||
border-left: 8px solid transparent;
|
||||
}
|
||||
to {
|
||||
border-left: 8px solid #ffb6c1;
|
||||
}
|
||||
}
|
||||
|
||||
.chatchannels__channelslist {
|
||||
padding: 10px 0;
|
||||
flex-grow: 1;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
class ChatChannelsController < ApplicationController
|
||||
before_action :authenticate_user!, only: [:moderate]
|
||||
|
||||
def index
|
||||
if params[:state] == "unopened"
|
||||
render_unopened_json_response
|
||||
return
|
||||
else
|
||||
render_channels_html
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@chat_channel = ChatChannel.includes(:messages).find_by(id: params[:id])
|
||||
if @chat_channel
|
||||
|
|
@ -15,7 +24,8 @@ class ChatChannelsController < ApplicationController
|
|||
def open
|
||||
@chat_channel = ChatChannel.find(params[:id])
|
||||
raise unless @chat_channel.has_member?(current_user)
|
||||
@chat_channel.chat_channel_memberships.where(user_id: current_user.id).first.touch(:last_opened_at)
|
||||
membership = @chat_channel.chat_channel_memberships.where(user_id: current_user.id).first
|
||||
membership.update(last_opened_at: 1.seconds.from_now, has_unopened_messages: false)
|
||||
render json: { status: "success", channel: params[:id] }, status: 200
|
||||
end
|
||||
|
||||
|
|
@ -55,4 +65,30 @@ class ChatChannelsController < ApplicationController
|
|||
def chat_channel_params
|
||||
params.require(:chat_channel).permit(:command)
|
||||
end
|
||||
|
||||
def render_unopened_json_response
|
||||
if current_user.has_role?(:super_admin) || Rails.env.development?
|
||||
@chat_channels_memberships = current_user.
|
||||
chat_channel_memberships.
|
||||
where(has_unopened_messages: true).order("updated_at DESC")
|
||||
else
|
||||
@chat_channels_memberships = []
|
||||
end
|
||||
render "index.json"
|
||||
end
|
||||
|
||||
def render_channels_html
|
||||
@chat_channels = current_user.chat_channels.
|
||||
order("last_message_at DESC").
|
||||
includes(:chat_channel_memberships)
|
||||
@chat_channels.each do |channel|
|
||||
channel.current_user = current_user
|
||||
end
|
||||
slug = if params[:slug] && params[:slug].start_with?("@")
|
||||
[current_user.username, params[:slug].gsub("@", "")].sort.join("/")
|
||||
else
|
||||
params[:slug]
|
||||
end
|
||||
@active_channel = ChatChannel.find_by_slug(slug) || @chat_channels.first
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -41,22 +41,7 @@ class PagesController < ApplicationController
|
|||
def live
|
||||
@chat_channels = [ChatChannel.find_by_channel_name("Workshop")].to_json
|
||||
end
|
||||
|
||||
def chat
|
||||
@chat_channels = current_user.chat_channels.
|
||||
order("last_message_at DESC").
|
||||
includes(:chat_channel_memberships)
|
||||
@chat_channels.each do |channel|
|
||||
channel.current_user = current_user
|
||||
end
|
||||
slug = if params[:slug] && params[:slug].start_with?("@")
|
||||
[current_user.username, params[:slug].gsub("@", "")].sort.join("/")
|
||||
else
|
||||
params[:slug]
|
||||
end
|
||||
@active_channel = ChatChannel.find_by_slug(slug) || @chat_channels.first
|
||||
end
|
||||
|
||||
|
||||
private # helpers
|
||||
|
||||
def latest_published_welcome_thread
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ module ApplicationHelper
|
|||
controller_name == "registrations" ||
|
||||
controller_name == "users" ||
|
||||
controller_name == "pages" ||
|
||||
|
||||
controller_name == "chat_channels" ||
|
||||
controller_name == "dashboards"||
|
||||
controller_name == "moderations"||
|
||||
controller_name == "videos"||
|
||||
|
|
|
|||
|
|
@ -9,19 +9,22 @@ const Channels = ({ activeChannelId, chatChannels, handleSwitchChannel }) => {
|
|||
: 'chatchanneltab--inactive';
|
||||
const name = channel.channel_type === "direct" ? '@'+channel.slug.replace(`${window.currentUser.username}/`, '').replace(`/${window.currentUser.username}`, '') : channel.channel_name
|
||||
const newMessagesIndicatorClass = new Date(channel.last_opened_at) < new Date(channel.last_message_at) ? "chatchanneltabindicator--new" : "chatchanneltabindicator--old"
|
||||
const modififedSlug = channel.channel_type === "direct" ? name : channel.slug;
|
||||
return (
|
||||
<button
|
||||
className='chatchanneltabbutton'
|
||||
onClick={handleSwitchChannel}
|
||||
data-channel-id={channel.id}
|
||||
data-channel-name={name}
|
||||
data-channel-slug={modififedSlug}
|
||||
>
|
||||
<span className={`chatchanneltab ${otherClassname}`}
|
||||
onClick={handleSwitchChannel}
|
||||
data-channel-id={channel.id}
|
||||
data-channel-name={name}
|
||||
data-channel-slug={modififedSlug}
|
||||
>
|
||||
<span className={"chatchanneltabindicator " + newMessagesIndicatorClass}></span> {name}
|
||||
<span
|
||||
data-channel-slug={modififedSlug}
|
||||
className={"chatchanneltabindicator " + newMessagesIndicatorClass}
|
||||
></span> {name}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -162,12 +162,15 @@ export default class Chat extends Component {
|
|||
handleSwitchChannel = e => {
|
||||
e.preventDefault();
|
||||
this.setState({
|
||||
activeChannelId: e.target.dataset.channelId,
|
||||
activeChannelId: parseInt(e.target.dataset.channelId),
|
||||
scrolled: false,
|
||||
showAlert: false,
|
||||
});
|
||||
window.history.replaceState(null, null, "/chat/"+e.target.dataset.channelName);
|
||||
window.history.replaceState(null, null, "/chat/"+e.target.dataset.channelSlug);
|
||||
document.getElementById("messageform").focus();
|
||||
if (window.ga && ga.create) {
|
||||
ga('send', 'pageview', location.pathname + location.search);
|
||||
}
|
||||
sendOpen(
|
||||
e.target.dataset.channelId,
|
||||
this.handleChannelOpenSuccess,
|
||||
|
|
@ -258,7 +261,9 @@ export default class Chat extends Component {
|
|||
return (
|
||||
<div className="chat">
|
||||
{this.renderChatChannels()}
|
||||
<div className="chat__activechat">{this.renderActiveChatChannel()}</div>
|
||||
<div className="chat__activechat">
|
||||
{this.renderActiveChatChannel()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { h, render } from 'preact';
|
||||
import Onboarding from '../src/Onboarding';
|
||||
import { getUserData } from '../src/utils/getUserData';
|
||||
import getUnopenedChannels from '../src/utils/getUnopenedChannels';
|
||||
|
||||
HTMLDocument.prototype.ready = new Promise(resolve => {
|
||||
if (document.readyState !== 'loading') {
|
||||
|
|
@ -22,12 +23,13 @@ function renderPage() {
|
|||
if (shouldShowOnboarding()) {
|
||||
setTimeout(() => {
|
||||
render(<Onboarding />, document.getElementById('top-bar'));
|
||||
}, 500);
|
||||
}, 580);
|
||||
}
|
||||
}
|
||||
|
||||
document.ready.then(
|
||||
getUserData().then(() => {
|
||||
renderPage();
|
||||
getUnopenedChannels();
|
||||
}),
|
||||
);
|
||||
|
|
|
|||
10
app/javascript/src/components/UnopenedChannelNotice.jsx
Normal file
10
app/javascript/src/components/UnopenedChannelNotice.jsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { h } from 'preact';
|
||||
|
||||
const UnopenedChannelNotice = () => {
|
||||
return (
|
||||
<div style={{position: 'absolute'}}>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UnopenedChannelNotice;
|
||||
74
app/javascript/src/utils/getUnopenedChannels.jsx
Normal file
74
app/javascript/src/utils/getUnopenedChannels.jsx
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
import { h, render, Component } from 'preact';
|
||||
|
||||
class UnopenedChannelNotice extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { visible: true }
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick() {
|
||||
this.setState({visible: false})
|
||||
}
|
||||
render() {
|
||||
if (this.state.visible) {
|
||||
const channels = this.props.channels.map(channel => {
|
||||
return <a
|
||||
href={"/chat/"+channel.adjusted_slug}
|
||||
style={{
|
||||
background: "#66e2d5",
|
||||
color: "black",
|
||||
padding: "2px 7px",
|
||||
display: "inline-block",
|
||||
margin: "3px 6px",
|
||||
borderRadius: "3px"}}>{channel.adjusted_slug}</a>
|
||||
});
|
||||
return (
|
||||
<div
|
||||
onClick={this.handleClick}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
zIndex: '200',
|
||||
top: '44px',
|
||||
right: 0,
|
||||
left: 0,
|
||||
background: '#333333',
|
||||
borderBottom: '1px solid black',
|
||||
color: 'white',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
fontSize: '15px',
|
||||
opacity: '0.94',
|
||||
padding: '12px 5px 3px'}}>
|
||||
<span style={{
|
||||
fontSize: "24px",
|
||||
verticalAlign: "-4px",
|
||||
display: "inline-block",
|
||||
marginRight: "3px"}}>💌</span> New Message from {channels}
|
||||
<span style={{ color: "#fefa87"}}>(beta testing)</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function getUnopenedChannels(user, successCb) {
|
||||
if (location.pathname.startsWith("/chat")) {
|
||||
return
|
||||
}
|
||||
fetch('/chat_channels?state=unopened', {
|
||||
method: 'GET',
|
||||
credentials: 'same-origin',
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
if (json.length > 0) {
|
||||
render(<UnopenedChannelNotice channels={json} />, document.getElementById('message-notice'));
|
||||
} else {
|
||||
render(null, document.getElementById('message-notice'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
|
|
@ -332,6 +332,14 @@ class Article < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def async_score_calc
|
||||
update_column(:hotness_score, BlackBox.article_hotness_score(self))
|
||||
update_column(:spaminess_rating, BlackBox.calculate_spaminess(self))
|
||||
index! if published && tag_list.exclude?("hiring")
|
||||
end
|
||||
handle_asynchronously :async_score_calc
|
||||
|
||||
|
||||
private
|
||||
|
||||
# def send_to_moderator
|
||||
|
|
@ -454,13 +462,6 @@ class Article < ApplicationRecord
|
|||
self.spaminess_rating = 0 if new_record?
|
||||
end
|
||||
|
||||
def async_score_calc
|
||||
update_column(:hotness_score, BlackBox.article_hotness_score(self))
|
||||
update_column(:spaminess_rating, BlackBox.calculate_spaminess(self))
|
||||
index! if published && tag_list.exclude?("hiring")
|
||||
end
|
||||
handle_asynchronously :async_score_calc
|
||||
|
||||
def async_bust
|
||||
CacheBuster.new.bust_article(self)
|
||||
HTTParty.get GeneratedImage.new(self).social_image if published
|
||||
|
|
|
|||
|
|
@ -44,4 +44,13 @@ class ChatChannel < ApplicationRecord
|
|||
ChatChannelMembership.create!(user_id: user.id, chat_channel_id: id)
|
||||
end
|
||||
end
|
||||
|
||||
def adjusted_slug(user = nil)
|
||||
user ||= current_user
|
||||
if channel_type == "direct"
|
||||
"@"+slug.gsub("/#{user.username}","").gsub("#{user.username}/","")
|
||||
else
|
||||
slug
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ class Message < ApplicationRecord
|
|||
before_validation :evaluate_markdown
|
||||
before_validation :evaluate_channel_permission
|
||||
after_create :update_chat_channel_last_message_at
|
||||
after_create :update_all_has_unopened_messages_statuses
|
||||
|
||||
def preferred_user_color
|
||||
color_options = [user.bg_color_hex || "#000000", user.text_color_hex || "#000000"]
|
||||
|
|
@ -20,6 +21,16 @@ class Message < ApplicationRecord
|
|||
chat_channel.touch(:last_message_at)
|
||||
end
|
||||
|
||||
def update_all_has_unopened_messages_statuses
|
||||
chat_channel.
|
||||
chat_channel_memberships.
|
||||
where("last_opened_at < ?", 1.seconds.ago).
|
||||
where.
|
||||
not(user_id: user_id).
|
||||
update_all(has_unopened_messages: true)
|
||||
end
|
||||
# handle_asynchronously :update_all_has_unopened_messages_statuses
|
||||
|
||||
def evaluate_markdown
|
||||
self.message_html = message_markdown
|
||||
end
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ class Reaction < ApplicationRecord
|
|||
|
||||
def update_reactable
|
||||
if reactable_type == "Article"
|
||||
reactable.async_score_calc
|
||||
reactable.index!
|
||||
CacheBuster.new.bust "/reactions/logged_out_reaction_counts?article_id=#{reactable_id}"
|
||||
elsif reactable_type == "Comment"
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@
|
|||
height: calc(100vh - 115px);
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="inner-content">
|
||||
<div class="chat-page-header">DEV Chat is Beta —— Post any thoughts in the "Meta" channel</div>
|
||||
<div
|
||||
id="chat"
|
||||
class="live-chat"
|
||||
3
app/views/chat_channels/index.json.jbuilder
Normal file
3
app/views/chat_channels/index.json.jbuilder
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
json.array! @chat_channels_memberships do |membership|
|
||||
json.adjusted_slug membership.chat_channel.adjusted_slug(current_user)
|
||||
end
|
||||
|
|
@ -75,6 +75,7 @@
|
|||
<% cache("application-top-bar--#{user_signed_in?}--#{is_internal_navigation?}", :expires_in => 100.hours) do %>
|
||||
<%= render "layouts/top_bar" unless is_internal_navigation? %>
|
||||
<% end %>
|
||||
<div id="message-notice"></div>
|
||||
<div id="page-content" class="universal-page-content-wrapper <%= view_class %>" data-current-page="<%= current_page %>">
|
||||
<div id="page-content-inner">
|
||||
<%= yield %>
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ Rails.application.config.assets.precompile += %w( favicon.ico )
|
|||
Rails.application.config.assets.precompile += %w( minimal.css )
|
||||
Rails.application.config.assets.precompile += %w( s3_direct_upload.css )
|
||||
Rails.application.config.assets.precompile += %w( base.js )
|
||||
Rails.application.config.assets.precompile += %w( pack.js )
|
||||
Rails.application.config.assets.precompile += %w( chat.js )
|
||||
Rails.application.config.assets.precompile += %w( s3_direct_upload.js )
|
||||
|
||||
Rails.application.config.assets.precompile += %w( lib/xss.js )
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ Rails.application.routes.draw do
|
|||
end
|
||||
|
||||
resources :messages, only: [:create]
|
||||
resources :chat_channels, only: [:show]
|
||||
resources :chat_channels, only: [:index, :show]
|
||||
resources :articles, only: [:update,:create,:destroy]
|
||||
resources :comments, only:[:create,:update,:destroy]
|
||||
resources :users, only:[:update]
|
||||
|
|
@ -97,8 +97,11 @@ Rails.application.routes.draw do
|
|||
get "/notifications/:username" => "notifications#index"
|
||||
patch "/onboarding_update" => "users#onboarding_update"
|
||||
get "email_subscriptions/unsubscribe"
|
||||
post "chat_channels/:id/moderate" => "chat_channels#moderate"
|
||||
post "chat_channels/:id/open" => "chat_channels#open"
|
||||
post "/chat_channels/:id/moderate" => "chat_channels#moderate"
|
||||
post "/chat_channels/:id/open" => "chat_channels#open"
|
||||
get "/chat" => "chat_channels#index"
|
||||
get "/chat/:slug" => "chat_channels#index"
|
||||
|
||||
# resources :users
|
||||
|
||||
get "/social_previews/article/:id" => "social_previews#article"
|
||||
|
|
@ -154,10 +157,6 @@ Rails.application.routes.draw do
|
|||
get "/infiniteloop" => "pages#infinite_loop"
|
||||
get "/faq" => "pages#faq"
|
||||
get "/live" => "pages#live"
|
||||
get "/chat" => "pages#chat"
|
||||
get "/chat/:slug" => "pages#chat"
|
||||
get "/m" => "pages#chat"
|
||||
get "/m/:slug" => "pages#chat"
|
||||
get "/swagnets" => "pages#swagnets"
|
||||
get "/welcome" => "pages#welcome"
|
||||
get "/💸", to: redirect("t/hiring")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
class AddNewMessagesToChatMemberships < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_column :chat_channel_memberships, :has_unopened_messages, :boolean, default: false
|
||||
end
|
||||
end
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20180604200603) do
|
||||
ActiveRecord::Schema.define(version: 20180606155327) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
|
|
@ -165,6 +165,7 @@ ActiveRecord::Schema.define(version: 20180604200603) do
|
|||
create_table "chat_channel_memberships", force: :cascade do |t|
|
||||
t.bigint "chat_channel_id", null: false
|
||||
t.datetime "created_at", null: false
|
||||
t.boolean "has_unopened_messages", default: false
|
||||
t.datetime "last_opened_at", default: "2017-01-01 05:00:00"
|
||||
t.datetime "updated_at", null: false
|
||||
t.bigint "user_id", null: false
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue