Updates to frontend and fix a bug where first socket assignment failed
This commit is contained in:
parent
d9943b201d
commit
f6b262ea66
3
.gitignore
vendored
3
.gitignore
vendored
@ -4,5 +4,6 @@
|
||||
erl_crash.dump
|
||||
*.ez
|
||||
/assets/node_modules/
|
||||
/priv/static/
|
||||
/priv/static/uploads
|
||||
/priv/static/assets
|
||||
/deps
|
||||
|
@ -126,115 +126,16 @@
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.circle {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
display: flex; /* or inline-flex */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Chat css from: https://www.bootdey.com/snippets/view/card-chat-list#html */
|
||||
.chat-container {
|
||||
height: 300px;
|
||||
.chat {
|
||||
max-height: 50vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.chat-list {
|
||||
padding: 0;
|
||||
font-size: .8rem;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.chat-list li {
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.chat-list .chat-img {
|
||||
float: left;
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.chat-list .chat-img img {
|
||||
-webkit-border-radius: 50px;
|
||||
-moz-border-radius: 50px;
|
||||
border-radius: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.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-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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
.card .card-header {
|
||||
background: #17202b;
|
||||
border: 0;
|
||||
font-size: 1rem;
|
||||
padding: .65rem 1rem;
|
||||
position: relative;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.content{
|
||||
margin-top:40px;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// We import the CSS which is extracted to its own file by esbuild.
|
||||
// Remove this line if you add a your own CSS build pipeline (e.g postcss).
|
||||
import "../css/app.css"
|
||||
import "../css/app.css";
|
||||
|
||||
// If you want to use Phoenix channels, run `mix help phx.gen.channel`
|
||||
// to get started and then uncomment the line below.
|
||||
@ -20,32 +20,32 @@ import "../css/app.css"
|
||||
//
|
||||
|
||||
// Include phoenix_html to handle method=PUT/DELETE in forms and buttons.
|
||||
import "phoenix_html"
|
||||
import "phoenix_html";
|
||||
// Establish Phoenix Socket and LiveView configuration.
|
||||
import {Socket} from "phoenix"
|
||||
import {LiveSocket} from "phoenix_live_view"
|
||||
import topbar from "../vendor/topbar"
|
||||
import {Socket} from "phoenix";
|
||||
import {LiveSocket} from "phoenix_live_view";
|
||||
import topbar from "../vendor/topbar";
|
||||
|
||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
|
||||
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}})
|
||||
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content");
|
||||
let liveSocket = new LiveSocket("/live", Socket, {params: {_csrf_token: csrfToken}});
|
||||
|
||||
// Show progress bar on live navigation and form submits
|
||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"})
|
||||
window.addEventListener("phx:page-loading-start", info => topbar.show())
|
||||
window.addEventListener("phx:page-loading-stop", info => topbar.hide())
|
||||
topbar.config({barColors: {0: "#29d"}, shadowColor: "rgba(0, 0, 0, .3)"});
|
||||
window.addEventListener("phx:page-loading-start", info => topbar.show());
|
||||
window.addEventListener("phx:page-loading-stop", info => topbar.hide());
|
||||
|
||||
// connect if there are any LiveViews on the page
|
||||
liveSocket.connect()
|
||||
liveSocket.connect();
|
||||
|
||||
// expose liveSocket on window for web console debug logs and latency simulation:
|
||||
// >> liveSocket.enableDebug()
|
||||
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
|
||||
// >> liveSocket.disableLatencySim()
|
||||
window.liveSocket = liveSocket
|
||||
window.liveSocket = liveSocket;
|
||||
|
||||
// Hack to remove alerts on click
|
||||
Array.from(window.document.getElementsByClassName('alert')).forEach((x) => x.addEventListener('click', () => x.style.display = "none"))
|
||||
Array.from(window.document.getElementsByClassName('alert')).forEach((x) => x.addEventListener('click', () => x.style.display = "none"));
|
||||
|
||||
import RoomChat from "./chat"
|
||||
window.userSocket = new Socket("/socket", {params: {_csrf_token: csrfToken}});
|
||||
import RoomChat from "./chat";
|
||||
window.RoomChat = RoomChat;
|
||||
window.userSocket = new Socket("/socket", {params: {_csrf_token: csrfToken}})
|
@ -1,11 +1,110 @@
|
||||
let RoomChat = {
|
||||
connect(socket, postId) {
|
||||
let channel = socket.channel(`post:${postId}`)
|
||||
channel.join()
|
||||
.receive("ok", resp => { console.log("Joined successfully: ", resp) })
|
||||
.receive("error", resp => { console.log("Unable to join: ", resp) })
|
||||
return channel;
|
||||
},
|
||||
}
|
||||
const gruvboxColors = [
|
||||
"#b8bb26",
|
||||
"#fabd2f",
|
||||
"#83a598",
|
||||
"#d3869b",
|
||||
"#8ec07c",
|
||||
"#458588",
|
||||
"#cc241d",
|
||||
"#d65d0e",
|
||||
"#bdae93",
|
||||
];
|
||||
const generateGruvboxFromString = (string) =>
|
||||
gruvboxColors[Array.from(string).map((x) => x.charCodeAt(0)).reduce((a, x) => a+x, 0) % gruvboxColors.length];
|
||||
|
||||
export default RoomChat;
|
||||
const RoomChat = (() => {
|
||||
let channel;
|
||||
const connect = (socket, postId) => {
|
||||
channel = socket.channel(`post:${postId}`);
|
||||
channel.join()
|
||||
.receive("ok", resp => { console.log("Joined successfully: ", resp); })
|
||||
.receive("error", resp => { console.log("Unable to join: ", resp); });
|
||||
return channel;
|
||||
};
|
||||
|
||||
const scrollToBottom = (element) => {
|
||||
element.scrollTop = element.scrollHeight;
|
||||
};
|
||||
|
||||
const appendComment = ({user, body, id, user_id, inserted_at}, element) => {
|
||||
const messageElement = document.createElement("div");
|
||||
messageElement.innerHTML = `
|
||||
<div class="d-flex flex-row card border rounded m-2 align-items-center">
|
||||
<div class="m-2">
|
||||
<div class="circle" style="background:${generateGruvboxFromString(user)}">${user.charAt(0)}</div>
|
||||
</div>
|
||||
<div class="m-2">
|
||||
<div class="comment">
|
||||
<div class="comment-header">
|
||||
<span class="comment-username">${user}</span>
|
||||
<span class="text-muted">${new Date(inserted_at).toLocaleString()}</span>
|
||||
</div>
|
||||
<div class="comment-body">
|
||||
${body}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
element.appendChild(messageElement);
|
||||
scrollToBottom(element);
|
||||
};
|
||||
|
||||
const leaveChannel = () => {
|
||||
if (channel) {
|
||||
channel.leave();
|
||||
console.log(channel);
|
||||
}
|
||||
};
|
||||
|
||||
const main = (post_id) => {
|
||||
leaveChannel();
|
||||
const chatWindow = document.getElementById("chat");
|
||||
window.userSocket.connect();
|
||||
channel = connect(window.userSocket, post_id);
|
||||
|
||||
channel.on('shout', (comment) => {
|
||||
appendComment(comment, chatWindow);
|
||||
});
|
||||
|
||||
channel.on('initial-comments', ({comments}) => {
|
||||
comments.forEach((comment) => {
|
||||
appendComment(comment, chatWindow);
|
||||
});
|
||||
scrollToBottom(chatWindow);
|
||||
});
|
||||
|
||||
channel.on('join', ({ user }) => {
|
||||
const joinElement = document.createElement("div");
|
||||
joinElement.innerHTML = `
|
||||
<div class="m-2 card border rounded p-2 text-muted">
|
||||
join: ${user}
|
||||
</div>
|
||||
`;
|
||||
chatWindow.appendChild(joinElement);
|
||||
scrollToBottom(chatWindow);
|
||||
});
|
||||
|
||||
channel.on('left', ({ user }) => {
|
||||
console.log(user, "left");
|
||||
});
|
||||
};
|
||||
|
||||
const submitForm = (e) => {
|
||||
e.preventDefault();
|
||||
let message = e.target.elements.message.value;
|
||||
if (message) {
|
||||
channel.push("send", {body: message});
|
||||
e.target.elements.message.value = "";
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return { main, submitForm };
|
||||
})();
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
window.addEventListener('phx:initial-post', (e) => RoomChat.main(e.detail.id));
|
||||
});
|
||||
|
||||
export default RoomChat;
|
||||
|
@ -24,13 +24,14 @@ defmodule AggieditWeb.PostChannel do
|
||||
|> Enum.map(fn c -> Aggiedit.Post.Comment.serialize(c) end)
|
||||
push(socket, "initial-comments", %{:comments => comments})
|
||||
|
||||
broadcast!(socket, "join", %{user: socket.assigns.current_user.username})
|
||||
{:noreply, socket}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_in("send", %{"body" => comment}=body, socket) do
|
||||
def handle_in("send", %{"body" => comment}, 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}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -39,7 +39,9 @@ defmodule AggieditWeb.PostLive.FormComponent do
|
||||
filename = "#{upload.uuid}.#{extension}"
|
||||
|
||||
dest = Path.join("priv/static/uploads", filename)
|
||||
File.cp!(data.path, dest)
|
||||
with :ok <- File.mkdir_p(Path.dirname(dest)) do
|
||||
File.cp!(data.path, dest)
|
||||
end
|
||||
|
||||
{:ok, upload} = Uploads.create_upload(%{
|
||||
file: filename,
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
<div class="d-flex justify-content-center">
|
||||
<div class="container">
|
||||
<div>
|
||||
@ -6,7 +5,7 @@
|
||||
</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"/>
|
||||
<img src={Routes.static_path(@socket, "/uploads/#{@post.upload.file}")} class="img-fluid" style="max-height: 40vh"/>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="post-body">
|
||||
@ -16,21 +15,18 @@
|
||||
<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="card">
|
||||
<div class="card-header">Chat</div>
|
||||
<div class="card-body chat-container">
|
||||
<ul class="chat-list" id="chat">
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border rounded p-2 m-2">
|
||||
<div class="chat" id="chat">
|
||||
</div>
|
||||
</div>
|
||||
<form class="border rounded p-2 m-2" onsubmit="return RoomChat.submitForm(event)">
|
||||
<div class="form-group m-2">
|
||||
<label for="message">Message</label>
|
||||
<input type="text" class="form-control" id="message" name="message" placeholder="Message">
|
||||
</div>
|
||||
<button type="submit" class="m-2 btn btn-primary">Submit</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -47,45 +43,3 @@
|
||||
/>
|
||||
</.modal>
|
||||
<% end %>
|
||||
|
||||
<script>
|
||||
const scrollToBottom = (element) => {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'end' });
|
||||
};
|
||||
|
||||
const appendComment = ({user, body, id, user_id, inserted_at}, element) => {
|
||||
const messageElement = document.createElement("div");
|
||||
messageElement.innerHTML = `
|
||||
<li class="${user_id == <%= @current_user.id %> ? 'out' : 'in'}" id=${id}>
|
||||
<div class="chat-body">
|
||||
<div class="chat-message">
|
||||
<h5>${user}</h5>
|
||||
<p>${body}</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
`;
|
||||
element.appendChild(messageElement);
|
||||
scrollToBottom(element);
|
||||
};
|
||||
|
||||
|
||||
let channel;
|
||||
window.addEventListener('phx:initial-post', (e) => {
|
||||
const chatWindow = document.getElementById("chat");
|
||||
window.userSocket.connect();
|
||||
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>
|
||||
|
@ -8,6 +8,7 @@
|
||||
<%= live_title_tag assigns[:page_title] || "Aggiedit" %>
|
||||
<link phx-track-static rel="stylesheet" href={Routes.static_path(@conn, "/assets/app.css")}/>
|
||||
<script phx-track-static type="text/javascript" src={Routes.static_path(@conn, "/assets/app.js")}></script>
|
||||
<link rel="icon" type="image/x-icon" href={Routes.static_path(@conn, "/favicon.ico")}/>
|
||||
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
|
||||
|
6
priv/static/cache_manifest.json
Normal file
6
priv/static/cache_manifest.json
Normal file
File diff suppressed because one or more lines are too long
BIN
priv/static/favicon.ico
Normal file
BIN
priv/static/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
Loading…
Reference in New Issue
Block a user