Initial chat box; man phoenix does not have the best docs
This commit is contained in:
parent
3cf9f4a364
commit
763ea5331b
@ -121,3 +121,103 @@
|
|||||||
max-width: 100px;
|
max-width: 100px;
|
||||||
max-height: 100px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.post-body {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Chat css from: https://www.bootdey.com/snippets/view/card-chat-list#html */
|
||||||
|
.chat-list {
|
||||||
|
padding: 0;
|
||||||
|
font-size: .8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list li {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
overflow: auto;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .chat-message {
|
||||||
|
-webkit-border-radius: 50px;
|
||||||
|
-moz-border-radius: 50px;
|
||||||
|
border-radius: 50px;
|
||||||
|
background: #5a99ee;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .chat-message:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 15px;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .chat-message h5 {
|
||||||
|
margin: 0 0 5px 0;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 100%;
|
||||||
|
font-size: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .chat-message p {
|
||||||
|
line-height: 18px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .chat-card-body {
|
||||||
|
margin-left: 20px;
|
||||||
|
float: left;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .in .chat-message:before {
|
||||||
|
left: -12px;
|
||||||
|
border-bottom: 20px solid transparent;
|
||||||
|
border-right: 20px solid #5a99ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .out .chat-img {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .out .chat-body {
|
||||||
|
float: right;
|
||||||
|
margin-right: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .out .chat-message {
|
||||||
|
background: #fc6d4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-list .out .chat-message:before {
|
||||||
|
right: -12px;
|
||||||
|
border-bottom: 20px solid transparent;
|
||||||
|
border-left: 20px solid #fc6d4c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-card {
|
||||||
|
overflow-y: scroll;
|
||||||
|
max-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-card .card-header:first-child {
|
||||||
|
-webkit-border-radius: 0.3rem 0.3rem 0 0;
|
||||||
|
-moz-border-radius: 0.3rem 0.3rem 0 0;
|
||||||
|
border-radius: 0.3rem 0.3rem 0 0;
|
||||||
|
}
|
||||||
|
.chat-card .card-header {
|
||||||
|
background: #17202b;
|
||||||
|
border: 0;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: .65rem 1rem;
|
||||||
|
position: relative;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
@ -1,46 +1,11 @@
|
|||||||
let RoomChat = {
|
let RoomChat = {
|
||||||
init(socket, postId) {
|
connect(socket, postId) {
|
||||||
console.log(postId);
|
|
||||||
console.log(socket);
|
|
||||||
let channel = socket.channel(`post:${postId}`)
|
let channel = socket.channel(`post:${postId}`)
|
||||||
channel.join()
|
channel.join()
|
||||||
.receive("ok", resp => { console.log("Joined successfully", resp) })
|
.receive("ok", resp => { console.log("Joined successfully: ", resp) })
|
||||||
.receive("error", resp => { console.log("Unable to join", resp) })
|
.receive("error", resp => { console.log("Unable to join: ", resp) })
|
||||||
this.listenForChats(channel)
|
return channel;
|
||||||
},
|
},
|
||||||
addMessage(user, message) {
|
|
||||||
// let body = `<span class="username"><b>${user}</b></span>: ${message}<br>`
|
|
||||||
// if (message.match(new RegExp(`@${window.userName}`, "ig"))) {
|
|
||||||
// $("#chat-box").append('<p class="chat-entry"><span class="mentioned">' + body + '</span></p>')
|
|
||||||
// } else {
|
|
||||||
// $("#chat-box").append('<p class="chat-entry">' + body + '</p>')
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
scrollBottom() {
|
|
||||||
// $("#chat-box").animate({ scrollTop: $('#chat-box').prop("scrollHeight")}, 200)
|
|
||||||
},
|
|
||||||
listenForChats(channel) {
|
|
||||||
channel.push('send', { body: "HELLO"});
|
|
||||||
// $(() => {
|
|
||||||
// $("#chat-form").on("submit", function(ev) {
|
|
||||||
// ev.preventDefault()
|
|
||||||
//
|
|
||||||
// let userMsg = $('#user-msg').val()
|
|
||||||
// channel.push('send', {body: userMsg})
|
|
||||||
//
|
|
||||||
// $("#user-msg").val("")
|
|
||||||
// })
|
|
||||||
|
|
||||||
// channel.on('shout', function(payload) {
|
|
||||||
// console.log(payload)
|
|
||||||
// RoomChat.addMessage(payload.name, payload.body)
|
|
||||||
// RoomChat.scrollBottom()
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
channel.on('shout', function(payload) {
|
|
||||||
console.log(payload)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RoomChat;
|
export default RoomChat;
|
@ -94,7 +94,7 @@ defmodule Aggiedit.Rooms do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def vote_count(post) do
|
def vote_count(post) do
|
||||||
votes = post
|
post
|
||||||
|> Repo.preload(:votes)
|
|> Repo.preload(:votes)
|
||||||
|> Map.get(:votes)
|
|> Map.get(:votes)
|
||||||
|> Enum.map(fn vote -> if vote.is_up, do: 1, else: -1 end)
|
|> Enum.map(fn vote -> if vote.is_up, do: 1, else: -1 end)
|
||||||
@ -105,11 +105,17 @@ defmodule Aggiedit.Rooms do
|
|||||||
is_up = direction == "upvote"
|
is_up = direction == "upvote"
|
||||||
vote = %Vote{is_up: is_up, user: user, post: post}
|
vote = %Vote{is_up: is_up, user: user, post: post}
|
||||||
|> Repo.insert(on_conflict: [set: [is_up: is_up]], conflict_target: [:user_id, :post_id])
|
|> Repo.insert(on_conflict: [set: [is_up: is_up]], conflict_target: [:user_id, :post_id])
|
||||||
|
|
||||||
post = change_post(post, %{score: vote_count(post)})
|
post = change_post(post, %{score: vote_count(post)})
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|
||||||
broadcast_post_over_room(post, :post_voted)
|
broadcast_post_over_room(post, :post_voted)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def comment_post(%Post{} = post, %User{} = user, comment) do
|
||||||
|
Repo.insert(%Comment{comment: comment, user: user, post: post})
|
||||||
|
end
|
||||||
|
|
||||||
defp broadcast_post_over_room({:error, _reason}=error, _event), do: error
|
defp broadcast_post_over_room({:error, _reason}=error, _event), do: error
|
||||||
defp broadcast_post_over_room({:ok, post}, event) do
|
defp broadcast_post_over_room({:ok, post}, event) do
|
||||||
PubSub.broadcast(Aggiedit.PubSub, "room:#{post.room_id}:posts", {event, post})
|
PubSub.broadcast(Aggiedit.PubSub, "room:#{post.room_id}:posts", {event, post})
|
||||||
|
@ -17,4 +17,8 @@ defmodule Aggiedit.Post.Comment do
|
|||||||
|> cast(attrs, [:comment])
|
|> cast(attrs, [:comment])
|
||||||
|> validate_required([:comment])
|
|> validate_required([:comment])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def serialize(c) do
|
||||||
|
%{"body" => c.comment, "user" => c.user.username, "id" => c.id, "inserted_at" => c.inserted_at}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,21 +2,35 @@ defmodule AggieditWeb.PostChannel do
|
|||||||
use AggieditWeb, :channel
|
use AggieditWeb, :channel
|
||||||
|
|
||||||
alias Aggiedit.Roles
|
alias Aggiedit.Roles
|
||||||
|
alias Aggiedit.Repo
|
||||||
alias Aggiedit.Rooms
|
alias Aggiedit.Rooms
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def join("post:" <> post_id, _payload, socket) do
|
def join("post:" <> post_id, _payload, socket) do
|
||||||
post = Rooms.get_post!(post_id)
|
post = Rooms.get_post!(post_id)
|
||||||
if Roles.guard?(socket.assigns.current_user, :show, post) do
|
if Roles.guard?(socket.assigns.current_user, :show, post) do
|
||||||
{:ok, socket}
|
send(self(), :after_join)
|
||||||
|
{:ok, assign(socket, %{:post => post})}
|
||||||
else
|
else
|
||||||
{:error, "You do not have permission to view this post."}
|
{:error, "You do not have permission to view this post."}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_in("send", body, socket) do
|
def handle_info(:after_join, socket) do
|
||||||
broadcast!(socket, "shout", body)
|
comments = socket.assigns.post
|
||||||
|
|> Repo.preload(comments: [:user])
|
||||||
|
|> Map.get(:comments)
|
||||||
|
|> Enum.map(fn c -> Aggiedit.Post.Comment.serialize(c) end)
|
||||||
|
push(socket, "initial-comments", %{:comments => comments})
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
|
def handle_in("send", %{"body" => comment}=body, socket) do
|
||||||
|
{:ok, comment} = Rooms.comment_post(socket.assigns.post, socket.assigns.current_user, comment)
|
||||||
|
broadcast!(socket, "shout", Aggiedit.Post.Comment.serialize(comment))
|
||||||
{:reply, :ok, socket}
|
{:reply, :ok, socket}
|
||||||
end
|
end
|
||||||
end
|
end
|
@ -15,10 +15,10 @@ defmodule AggieditWeb.PostLive.Show do
|
|||||||
post = Rooms.get_post!(id)
|
post = Rooms.get_post!(id)
|
||||||
|> Repo.preload(:upload)
|
|> Repo.preload(:upload)
|
||||||
if Roles.guard?(socket.assigns.current_user, socket.assigns.live_action, post) do
|
if Roles.guard?(socket.assigns.current_user, socket.assigns.live_action, post) do
|
||||||
{:noreply,
|
socket = (if socket.assigns.live_action == :show, do: push_event(socket, "initial-post", %{:id => post.id}), else: socket)
|
||||||
socket
|
|
||||||
|> assign(:page_title, page_title(socket.assigns.live_action))
|
|> assign(:page_title, page_title(socket.assigns.live_action))
|
||||||
|> assign(:post, post)}
|
|> assign(:post, post)
|
||||||
|
{:noreply, socket}
|
||||||
else
|
else
|
||||||
{:noreply, socket |> put_flash(:error, "You don't have permission to do that.") |> redirect(to: Routes.post_show_path(socket, :show, socket.assigns.room, post))}
|
{:noreply, socket |> put_flash(:error, "You don't have permission to do that.") |> redirect(to: Routes.post_show_path(socket, :show, socket.assigns.room, post))}
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,37 @@
|
|||||||
<h1>Show Post</h1>
|
|
||||||
|
<div class="d-flex justify-content-center">
|
||||||
|
<div class="container">
|
||||||
|
<div>
|
||||||
|
<h1><%= @post.title %></h1>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<%= if Ecto.assoc_loaded?(@post.upload) && !is_nil(@post.upload) do %>
|
||||||
|
<img src={Routes.static_path(@socket, "/uploads/#{@post.upload.file}")} class="img-fluid"/>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="post-body">
|
||||||
|
<%= @post.body %>
|
||||||
|
</div>
|
||||||
|
<%= if Aggiedit.Roles.guard?(@current_user, :edit, @post) do %>
|
||||||
|
<span><%= live_patch "Edit", to: Routes.post_show_path(@socket, :edit, @room, @post), class: "button" %></span> |
|
||||||
|
<% end %>
|
||||||
|
<span><%= live_redirect "Back", to: Routes.post_index_path(@socket, :index, @room) %></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- chat container from https://www.bootdey.com/snippets/view/card-chat-list#html -->
|
||||||
|
<div class="container content mt-2">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-6 col-lg-6 col-md-6 col-sm-12 col-12">
|
||||||
|
<div class="chat-card">
|
||||||
|
<div class="chat-card-body">
|
||||||
|
<ul class="chat-list" id="chat" phx-update="ignore">
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%= if @live_action in [:edit] do %>
|
<%= if @live_action in [:edit] do %>
|
||||||
<.modal return_to={Routes.post_show_path(@socket, :show, @room, @post)}>
|
<.modal return_to={Routes.post_show_path(@socket, :show, @room, @post)}>
|
||||||
@ -14,24 +47,43 @@
|
|||||||
</.modal>
|
</.modal>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<ul>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<strong>Title:</strong>
|
|
||||||
<%= @post.title %>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<li>
|
|
||||||
<strong>Body:</strong>
|
|
||||||
<%= @post.body %>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<span><%= live_patch "Edit", to: Routes.post_show_path(@socket, :edit, @room, @post), class: "button" %></span> |
|
|
||||||
<span><%= live_redirect "Back", to: Routes.post_index_path(@socket, :index, @room) %></span>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
window.userSocket.connect();
|
const scrollToBottom = (element) => {
|
||||||
window.RoomChat.init(window.userSocket, <%= @post.id %>)
|
element.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendComment = ({user, body, id, inserted_at}, element) => {
|
||||||
|
const messageElement = document.createElement("div");
|
||||||
|
messageElement.innerHTML = `
|
||||||
|
<li class="in" id=${id}>
|
||||||
|
<div class="chat-card-body">
|
||||||
|
<div class="chat-message">
|
||||||
|
<h5>${user}</h5>
|
||||||
|
<p>${body}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
`;
|
||||||
|
element.appendChild(messageElement);
|
||||||
|
scrollToBottom(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
window.addEventListener('phx:initial-post', (e) => {
|
||||||
|
const chatWindow = document.getElementById("chat");
|
||||||
|
window.userSocket.connect();
|
||||||
|
let channel = window.RoomChat.connect(window.userSocket, e.detail.id);
|
||||||
|
|
||||||
|
channel.on('shout', (comment) => {
|
||||||
|
appendComment(comment, chatWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
channel.on('initial-comments', ({comments}) => {
|
||||||
|
comments.forEach((comment) => {
|
||||||
|
appendComment(comment, chatWindow);
|
||||||
|
});
|
||||||
|
scrollToBottom(chatWindow);
|
||||||
|
});
|
||||||
|
channel.push("send", {body: "Hello!"});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user