Implement open inbox for dev-connect (#1563)
This commit is contained in:
parent
18a111418b
commit
d8709c0334
18 changed files with 347 additions and 23 deletions
|
|
@ -12,6 +12,7 @@ function callInitalizers(){
|
|||
if (document.getElementsByTagName('body')[0].getAttribute('data-loaded') == "true") {
|
||||
clearInterval(waitingForDataLoad);
|
||||
if (document.getElementsByTagName('body')[0].getAttribute('data-user-status') == "logged-in") {
|
||||
initializeAllChatButtons();
|
||||
initializeBaseUserData();
|
||||
}
|
||||
initializeReadingListPage();
|
||||
|
|
|
|||
106
app/assets/javascripts/initializers/initializeAllChatButtons.js
Normal file
106
app/assets/javascripts/initializers/initializeAllChatButtons.js
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
function initModal() {
|
||||
var modal = document.querySelector('.modal');
|
||||
modal.querySelector('.close-modal').addEventListener('click', toggleModal);
|
||||
modal.querySelector('.overlay').addEventListener('click', toggleModal);
|
||||
}
|
||||
|
||||
function toggleModal() {
|
||||
var modal = document.querySelector('.modal');
|
||||
var currentState = modal.style.display;
|
||||
|
||||
if (currentState === 'none') {
|
||||
showChatModal(modal);
|
||||
} else {
|
||||
hideChatModal(modal);
|
||||
}
|
||||
}
|
||||
|
||||
function showChatModal(modal) {
|
||||
modal.style.display = 'block';
|
||||
document.getElementById('new-message').focus();
|
||||
}
|
||||
|
||||
function hideChatModal(modal) {
|
||||
modal.style.display = 'none';
|
||||
}
|
||||
|
||||
function handleChatButtonPress(form) {
|
||||
var message = document.getElementById('new-message').value;
|
||||
var formDataInfo = JSON.parse(form.dataset.info);
|
||||
var formData = new FormData();
|
||||
|
||||
if (message.replace(/\s/g, '').length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
formData.append('user_id', formDataInfo.id);
|
||||
formData.append('message', message);
|
||||
formData.append('controller', 'chat_channels');
|
||||
|
||||
getCsrfToken()
|
||||
.then(sendFetch('chat-creation', formData))
|
||||
.then(() => {
|
||||
window.location.href = `/connect/@${formDataInfo.username}`;
|
||||
});
|
||||
}
|
||||
|
||||
function addButtonClickHandle(response, button, modalInfo) {
|
||||
var linkWrap = document.getElementById("user-connect-redirect");
|
||||
var form = document.getElementById('new-message-form');
|
||||
button.classList.add('showing');
|
||||
if (modalInfo.showChat === "open" && response !== "mutual") {
|
||||
linkWrap.removeAttribute("href") // remove link
|
||||
button.addEventListener('click', toggleModal);
|
||||
button.style.display = 'initial'; // show button
|
||||
linkWrap.style.display = 'initial'; // show button
|
||||
form.onsubmit = function() {handleChatButtonPress(form); return false;};
|
||||
} else if (response === 'mutual') {
|
||||
button.removeEventListener('click', toggleModal);
|
||||
button.style.display = 'initial'; // show button
|
||||
linkWrap.style.display = 'initial'; // show button
|
||||
}
|
||||
}
|
||||
|
||||
function fetchButton(button, modalInfo) {
|
||||
var dataRequester;
|
||||
// button.dataset.fetched = 'fetched'; // telling initialize that this button has been fetched
|
||||
if (window.XMLHttpRequest) {
|
||||
dataRequester = new XMLHttpRequest();
|
||||
} else {
|
||||
dataRequester = new ActiveXObject('Microsoft.XMLHTTP');
|
||||
}
|
||||
dataRequester.onreadystatechange = function() {
|
||||
if (dataRequester.readyState === XMLHttpRequest.DONE && dataRequester.status === 200) {
|
||||
addButtonClickHandle(dataRequester.response, button, modalInfo);
|
||||
}
|
||||
}
|
||||
dataRequester.open('GET', '/follows/' + modalInfo.id + '?followable_type=' + modalInfo.className);
|
||||
dataRequester.send();
|
||||
}
|
||||
|
||||
function initializeChatButton(button, modalInfo) {
|
||||
// if user logged out, do nothing
|
||||
var user = userData();
|
||||
if (user === null || user.id === modalInfo.id) {
|
||||
return;
|
||||
}
|
||||
fetchButton(button, modalInfo);
|
||||
}
|
||||
|
||||
// finds all elements with chat action button class
|
||||
function initializeAllChatButtons() {
|
||||
var buttons = document.getElementsByClassName('chat-action-button');
|
||||
var modal = document.getElementById('new-message-form');
|
||||
var i;
|
||||
|
||||
if (!modal) {
|
||||
return;
|
||||
}
|
||||
|
||||
var modalInfo = JSON.parse(modal.dataset.info);
|
||||
initModal();
|
||||
|
||||
for (i = 0; i < buttons.length; i += 1) {
|
||||
initializeChatButton(buttons[i], modalInfo);
|
||||
}
|
||||
}
|
||||
|
|
@ -73,9 +73,12 @@ function handleTagButtAssignment(user, butt, buttInfo) {
|
|||
|
||||
function assignInitialButtResponse(response, butt) {
|
||||
butt.classList.add('showing');
|
||||
if (response === 'true') {
|
||||
if (response === 'true' || response === 'mutual') {
|
||||
assignState(butt, 'unfollow');
|
||||
}
|
||||
else if (response === 'follow-back') {
|
||||
assignState(butt, 'follow-back')
|
||||
}
|
||||
else if (response === 'false') {
|
||||
assignState(butt, 'follow');
|
||||
}
|
||||
|
|
@ -114,7 +117,7 @@ function handleOptimisticButtRender(butt) {
|
|||
}catch (err) {return}
|
||||
});
|
||||
}catch (err) {return}
|
||||
|
||||
|
||||
handleFollowButtPress(butt);
|
||||
}
|
||||
}
|
||||
|
|
@ -129,15 +132,16 @@ function handleFollowButtPress(butt) {
|
|||
}
|
||||
|
||||
function assignState(butt, newState) {
|
||||
var style = '';
|
||||
if (butt.dataset.info) {
|
||||
style = JSON.parse(butt.dataset.info).style;
|
||||
}
|
||||
var style = JSON.parse(butt.dataset.info).style;
|
||||
butt.classList.add('showing');
|
||||
if (newState === 'follow') {
|
||||
if (newState === 'follow' || newState === 'follow-back') {
|
||||
butt.dataset.verb = 'unfollow';
|
||||
butt.classList.remove('following-butt');
|
||||
addFollowText(butt, style);
|
||||
if (newState === 'follow-back') {
|
||||
addFollowText(butt, newState);
|
||||
} else if (newState === 'follow') {
|
||||
addFollowText(butt, style);
|
||||
}
|
||||
} else if (newState === 'login') {
|
||||
addFollowText(butt, style);
|
||||
} else if (newState === 'self') {
|
||||
|
|
@ -167,4 +171,3 @@ function addFollowingText(butt, style) {
|
|||
butt.innerHTML = '✓ FOLLOWING';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,18 @@ function sendFetch(switchStatement, body) {
|
|||
credentials: 'same-origin',
|
||||
});
|
||||
};
|
||||
case 'chat-creation':
|
||||
return function(csrfToken) {
|
||||
body.append('authenticity_token', csrfToken);
|
||||
return window.fetch('/open_chat', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
},
|
||||
body: body,
|
||||
credentials: 'same-origin',
|
||||
});
|
||||
};
|
||||
case 'comment-creation':
|
||||
return function(csrfToken) {
|
||||
return window.fetch('/comments', {
|
||||
|
|
|
|||
|
|
@ -279,13 +279,13 @@
|
|||
text-align:left;
|
||||
}
|
||||
}
|
||||
.user-profile-follow-button-wrapper{
|
||||
.user-profile-follow-button-wrapper {
|
||||
position:relative;
|
||||
z-index:5;
|
||||
display:inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.user-profile-follow-button{
|
||||
.user-profile-follow-button {
|
||||
background: $green;
|
||||
color:white;
|
||||
border:0;
|
||||
|
|
@ -314,4 +314,135 @@
|
|||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
|
||||
// same as above, just add class to above style
|
||||
.user-profile-chat-button-wrapper {
|
||||
position:relative;
|
||||
z-index:5;
|
||||
display:inline-block;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.user-profile-chat-button {
|
||||
background: $green;
|
||||
color:white;
|
||||
border:0;
|
||||
font-size:17px;
|
||||
border-radius:3px;
|
||||
vertical-align:3px;
|
||||
padding:2px 6px;
|
||||
cursor:pointer;
|
||||
min-width: 60px;
|
||||
font-family: "HelveticaNeue-CondensedBold", "HelveticaNeueBoldCondensed", "HelveticaNeue-Bold-Condensed", "Helvetica Neue Bold Condensed", "HelveticaNeueBold", "HelveticaNeue-Bold", "Helvetica Neue Bold", "HelveticaNeue", "Helvetica Neue", "TeXGyreHerosCnBold", "Helvetica", "Tahoma", "Geneva", "Arial Narrow", "Arial", sans-serif;
|
||||
-webkit-appearance: none;
|
||||
font-stretch: condensed;
|
||||
&.showing{
|
||||
opacity:1;
|
||||
}
|
||||
@media screen and ( min-width: 430px ){
|
||||
font-size:18px;
|
||||
padding:3px 8px;
|
||||
vertical-align:7px;
|
||||
border-radius:5px;
|
||||
min-width: 80px;
|
||||
}
|
||||
@media screen and ( min-width: 950px ){
|
||||
font-size:22px;
|
||||
padding:4px 8px;
|
||||
vertical-align:12px;
|
||||
border-radius:5px;
|
||||
min-width: 80px;
|
||||
}
|
||||
}
|
||||
|
||||
// new message modal for open inbox
|
||||
.modal {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 990;
|
||||
}
|
||||
|
||||
.modal .overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 995;
|
||||
background: rgba(0,0,0,0.85);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
z-index: 999;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
max-height: 80%;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
border: 1px solid #d6d6d6;
|
||||
box-shadow: 3px 3px 0px #bababa;
|
||||
border-radius: 3px;
|
||||
max-width: 90%;
|
||||
width: 80%;
|
||||
|
||||
.message-form {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
align-content: stretch;
|
||||
|
||||
textarea {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #d6d6d6;
|
||||
font-size: 14px;
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
width: calc(100% - 100px);
|
||||
order: 1;
|
||||
flex-grow: 1;
|
||||
resize: vertical;
|
||||
max-height:30%;
|
||||
}
|
||||
|
||||
.submit-message {
|
||||
margin: 10px 10px 10px 0;
|
||||
border-radius: 3px;
|
||||
background: $dark-purple;
|
||||
font-weight: 600px;
|
||||
font-family: $helvetica-condensed;
|
||||
width: 80px;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
height: 80px;
|
||||
order: 10;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.close-modal {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
opacity: 0.5;
|
||||
background: none;
|
||||
border: none;
|
||||
padding: 0 0 0 10px;
|
||||
transition: opacity 0.2s ease;
|
||||
&:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,25 @@ class ChatChannelsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def open_chat
|
||||
chat_recipient = User.find(params[:user_id])
|
||||
if chat_recipient.inbox_type == "open"
|
||||
chat = ChatChannel.create_with_users([current_user, chat_recipient], "direct")
|
||||
# get message param to generate message to send
|
||||
# message_markdown = "Hi #{chat_recipient.username}! I am #{current_user.username}. I can message you on DEV Connect because your inbox is open."
|
||||
message_markdown = params[:message]
|
||||
message = Message.new(
|
||||
chat_channel: chat,
|
||||
message_markdown: message_markdown,
|
||||
user: current_user,
|
||||
)
|
||||
chat.messages.append(message)
|
||||
render json: { status: "success", message: "chat channel created!" }, status: 200
|
||||
else
|
||||
render json: { status: "error", message: "not allowed!" }, status: 400
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def chat_channel_params
|
||||
|
|
|
|||
|
|
@ -10,8 +10,13 @@ class FollowsController < ApplicationController
|
|||
if current_user.id == params[:id].to_i && params[:followable_type] == "User"
|
||||
render plain: "self"
|
||||
return
|
||||
elsif params[:followable_type] == "User" && FollowChecker.new(current_user, params[:followable_type], params[:id]).cached_follow_check && FollowChecker.new(User.find(params[:id]), params[:followable_type], current_user.id).cached_follow_check
|
||||
render plain: "mutual"
|
||||
elsif params[:followable_type] == "User" && FollowChecker.new(User.find(params[:id]), params[:followable_type], current_user.id).cached_follow_check
|
||||
render plain: "follow-back"
|
||||
else
|
||||
render plain: FollowChecker.new(current_user, params[:followable_type], params[:id]).cached_follow_check
|
||||
end
|
||||
render plain: FollowChecker.new(current_user, params[:followable_type], params[:id]).cached_follow_check
|
||||
end
|
||||
|
||||
def create
|
||||
|
|
|
|||
|
|
@ -131,6 +131,7 @@ class User < ApplicationRecord
|
|||
length: { maximum: 500 }
|
||||
validates :mentee_description, :mentor_description,
|
||||
length: { maximum: 1000 }
|
||||
validates :inbox_type, inclusion: { in: ["open", "private"] }
|
||||
validate :conditionally_validate_summary
|
||||
validate :validate_mastodon_url
|
||||
validate :validate_feed_url
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ class UserPolicy < ApplicationPolicy
|
|||
mostly_work_with
|
||||
name
|
||||
offering_mentorship
|
||||
inbox_type
|
||||
permit_adjacent_sponsors
|
||||
password
|
||||
password_confirmation
|
||||
|
|
|
|||
|
|
@ -40,11 +40,12 @@
|
|||
<% else %>
|
||||
|
||||
<% if @user.email_public &&
|
||||
@user.employer_name.present? &&
|
||||
@user.location.present? &&
|
||||
@user.education.present? &&
|
||||
@user.education.size > 25 &&
|
||||
@user.summary.to_s.size < 145 %>
|
||||
@user.employer_name.present? &&
|
||||
@user.location.present? &&
|
||||
@user.education.present? &&
|
||||
@user.education.size > 25 &&
|
||||
@user.summary.to_s.size < 145
|
||||
%>
|
||||
<style>
|
||||
.user-profile-header {
|
||||
min-height: 398px;
|
||||
|
|
|
|||
|
|
@ -144,3 +144,19 @@
|
|||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<% if false %>
|
||||
<h2> Connect </h2>
|
||||
|
||||
<h3> Inbox Type </h3>
|
||||
|
||||
<%= form_for(@user) do |f| %>
|
||||
<div class="field checkbox-label-first">
|
||||
<%= f.label :inbox_type, "Open your inbox to messages from people you don't follow or keep your inbox private to mutual follows."%>
|
||||
<div class="sub-field">
|
||||
<%= f.select :inbox_type, [['Open','open'],['Private','private']]%>
|
||||
</div>
|
||||
<%= f.submit "SUBMIT", class: "cta" %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
<span class="user-profile-follow-button-wrapper">
|
||||
<button id="user-follow-butt" class="cta follow-action-button user-profile-follow-button" style="color:<%= user_colors(@user)[:text] %>;background-color:<%= user_colors(@user)[:bg] %>" data-info='{"id":<%= @user.id %>,"className":"<%= @user.class.name %>"}'> </button>
|
||||
</span>
|
||||
<span class="user-profile-chat-button-wrapper">
|
||||
<a href="/connect/@<%= @user.username %>" id="user-connect-redirect" style="display:none">
|
||||
<button class="chat-action-button user-profile-chat-button" id="modal-opener" style="color:<%= user_colors(@user)[:text] %>;background-color:<%=user_colors(@user)[:bg] %>; display:none; ">CHAT</button>
|
||||
</a>
|
||||
</span>
|
||||
|
||||
</h1>
|
||||
<p class="profile-description" itemprop="description">
|
||||
<%= @user.summary.presence || ["404 bio not found"].sample %>
|
||||
|
|
@ -97,6 +103,16 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal" style="display: none">
|
||||
<div class="overlay"></div>
|
||||
<div class="modal-content " id="new-message-modal">
|
||||
<button title="Close" class="close-modal">X</button>
|
||||
<form id="new-message-form" class="message-form" data-info='{"id":<%= @user.id%>,"className":"<%= @user.class.name %>","username":"<%= @user.username %>", "showChat":"<%= @user.inbox_type %>"}'>
|
||||
<textarea id="new-message" rows="4" cols="70" placeholder="Enter your message here..."></textarea>
|
||||
<button type="submit" value="Submit" class="submit-message">SEND</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<%= render "articles/user_metadata", context: "profile" %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -141,6 +141,7 @@ Rails.application.routes.draw do
|
|||
post "/chat_channels/:id/open" => "chat_channels#open"
|
||||
get "/connect" => "chat_channels#index"
|
||||
get "/connect/:slug" => "chat_channels#index"
|
||||
post "/open_chat" => "chat_channels#open_chat"
|
||||
|
||||
post "/pusher/auth" => "pusher#auth"
|
||||
|
||||
|
|
|
|||
5
db/migrate/20190121172642_add_inbox_to_user.rb
Normal file
5
db/migrate/20190121172642_add_inbox_to_user.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class AddInboxToUser < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
add_column :users, :inbox, :string, default: "private"
|
||||
end
|
||||
end
|
||||
5
db/migrate/20190121191754_change_inbox_name.rb
Normal file
5
db/migrate/20190121191754_change_inbox_name.rb
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
class ChangeInboxName < ActiveRecord::Migration[5.1]
|
||||
def change
|
||||
rename_column :users, :inbox, :inbox_type
|
||||
end
|
||||
end
|
||||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
### Issue Levels
|
||||
|
||||
* Level 5: Mission critical. Tends to impact all users. (i.e. production stuck in an infinite heroku deployment)
|
||||
- Level 5: Mission critical. Tends to impact all users. (i.e. production stuck in an infinite heroku deployment)
|
||||
|
||||
* Level 4: Not mission critical but needs to be taken care of as quickly as possible. Tends to impact a subset of users. (i.e. form submission broken on mentorship settings)
|
||||
- Level 4: Not mission critical but needs to be taken care of as quickly as possible. Tends to impact a subset of users. (i.e. form submission broken on mentorship settings)
|
||||
|
||||
* Level 3: Issue that can wait until the next sprint planning, but definitely needs to be discussed. Often an edge case that sparks discussion -- is this a patch or do we need to re-architect something? (i.e. running out of memory where a few pages get 500 errors)
|
||||
- Level 3: Issue that can wait until the next sprint planning, but definitely needs to be discussed. Often an edge case that sparks discussion -- is this a patch or do we need to re-architect something? (i.e. running out of memory where a few pages get 500 errors)
|
||||
|
||||
* Level 2: Issue is not affecting users. Likely an internal tool that needs to be fixed. (i.e. slack notifications not pinging)
|
||||
- Level 2: Issue is not affecting users. Likely an internal tool that needs to be fixed. (i.e. slack notifications not pinging)
|
||||
|
||||
* Level 1: Tech that stopped working but also isn't related to users/the site. (i.e. the gumball machine we use to generate random winners is broken)
|
||||
- Level 1: Tech that stopped working but also isn't related to users/the site. (i.e. the gumball machine we use to generate random winners is broken)
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ RSpec.describe User, type: :model do
|
|||
it { is_expected.to validate_presence_of(:username) }
|
||||
it { is_expected.to validate_length_of(:username).is_at_most(30).is_at_least(2) }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(100) }
|
||||
it { is_expected.to validate_inclusion_of(:inbox_type).in_array(["open", "private"]) }
|
||||
end
|
||||
|
||||
# the followings are failing
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ RSpec.describe "Follows #show", type: :request do
|
|||
expect(response.body).to eq("not-logged-in")
|
||||
end
|
||||
|
||||
it "returns false when not followeing" do
|
||||
it "returns false when not following" do
|
||||
expect(get_following_status.uniq[0]).to eq("false")
|
||||
end
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue