Skip to content

Commit

Permalink
Move chat messages to their own structure
Browse files Browse the repository at this point in the history
  • Loading branch information
mreishus committed Nov 19, 2019
1 parent e9206b9 commit 0b54c16
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 23 deletions.
3 changes: 2 additions & 1 deletion backend/.iex.exs
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ alias SpadesGame.{
alias SpadesChat.{
Chat,
ChatServer,
ChatSupervisor
ChatSupervisor,
ChatMessage
}
34 changes: 34 additions & 0 deletions backend/lib/spades_chat/chat_message.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
defmodule SpadesChat.ChatMessage do
@moduledoc """
Represents a seat at a table.
The integers here are user ids.
"""

alias SpadesChat.{ChatMessage}

@derive Jason.Encoder
defstruct [:text, :sent_by, :when, :shortcode]

use Accessible

@type t :: %ChatMessage{
text: String.t(),
sent_by: integer | nil,
when: DateTime.t(),
shortcode: String.t()
}

@spec new(String.t(), integer | nil) :: ChatMessage.t()
def new(text, sent_by) do
%ChatMessage{
text: text,
sent_by: sent_by,
when: DateTime.utc_now(),
shortcode: shortcode()
}
end

defp shortcode() do
:crypto.strong_rand_bytes(6) |> Base.url_encode64()
end
end
19 changes: 16 additions & 3 deletions backend/lib/spades_web/channels/chat_channel.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ defmodule SpadesWeb.ChatChannel do
Channel for chatrooms
"""
use SpadesWeb, :channel
alias SpadesChat.{ChatServer, ChatSupervisor}
alias SpadesChat.{ChatMessage, ChatServer, ChatSupervisor}

def join("chat:" <> room_slug, _payload, socket) do
{:ok, _pid} = ChatSupervisor.start_chat_if_needed(room_slug)
Expand All @@ -17,8 +17,21 @@ defmodule SpadesWeb.ChatChannel do
{:ok, client_state(socket), socket}
end

def handle_in("message", %{"message" => message}, %{assigns: %{room_slug: room_slug}} = socket) do
messages = ChatServer.add_message(room_slug, message)
# New chat message typed from user
def handle_in(
"message",
%{"message" => message_text},
%{assigns: %{room_slug: room_slug, user_id: user_id}} = socket
) do
message = ChatMessage.new(message_text, user_id)

# Disallow anonymous message submission
messages =
if user_id != nil do
ChatServer.add_message(room_slug, message)
else
ChatServer.messages(room_slug)
end

socket =
socket
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/elixir_backend.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,11 @@ declare module "elixir-backend" {
public id: number;
public alias: string;
}

export declare class ChatMessage {
public text: string;
public sent_by: number | null;
public when: string; // Actually a timestamp
public shortcode: string;
}
}
10 changes: 5 additions & 5 deletions frontend/src/features/chat/Chat.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import React, { useState, useCallback } from "react";
//import cx from "classnames";
import ChatMessages from "./ChatMessages";
import ChatInput from "./ChatInput";
import useChannel from "../../hooks/useChannel";
import useIsLoggedIn from "../../hooks/useIsLoggedIn";
import { ChatMessage } from "elixir-backend";

interface Props {
roomName: string;
}

export const Chat: React.FC<Props> = ({ roomName }) => {
const [messages, setMessages] = useState<Array<any>>([]);
const isLoggedIn = useIsLoggedIn();
const [messages, setMessages] = useState<Array<ChatMessage>>([]);
const onChannelMessage = useCallback((event, payload) => {
if (
event === "phx_reply" &&
payload.response != null &&
payload.response.messages != null
) {
setMessages(payload.response.messages);
} else {
console.log("== Chat got (ignored) message", event, payload);
}
}, []);
const broadcast = useChannel(`chat:${roomName}`, onChannelMessage);
Expand All @@ -29,7 +29,7 @@ export const Chat: React.FC<Props> = ({ roomName }) => {
<div className="mb-2">
<ChatMessages messages={messages} />
</div>
<ChatInput broadcast={broadcast} />
{isLoggedIn && <ChatInput broadcast={broadcast} />}
</div>
);
};
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/features/chat/ChatLine.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from "react";
import UserName from "../user/UserName";
import { ChatMessage } from "elixir-backend";

interface Props {
message: ChatMessage;
}

export const ChatLine: React.FC<Props> = ({ message }) => {
return (
<div>
<span className="text-gray-500">&lt;</span>
<span className="text-blue-800">
<UserName userId={message.sent_by} />
</span>
<span className="text-gray-500">&gt;</span> {message.text}
</div>
);
};
export default ChatLine;
12 changes: 2 additions & 10 deletions frontend/src/features/chat/ChatMessages.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import React from "react";
import ScrollToBottom from "react-scroll-to-bottom";
import ChatMessagesInner from "./ChatMessagesInner";
//import React, { useState, useEffect, useContext } from "react";
//import cx from "classnames";
import { ChatMessage } from "elixir-backend";

interface Props {
messages: Array<string>;
messages: Array<ChatMessage>;
}

export const ChatMessages: React.FC<Props> = ({ messages }) => {
Expand All @@ -14,12 +13,5 @@ export const ChatMessages: React.FC<Props> = ({ messages }) => {
<ChatMessagesInner messages={messages} />
</ScrollToBottom>
);
/*
return (
<div className="bg-white border rounded max-w-md p-2 h-32 overflow-y-auto">
<ChatMessagesInner messages={messages} />
</div>
);
*/
};
export default ChatMessages;
8 changes: 4 additions & 4 deletions frontend/src/features/chat/ChatMessagesInner.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from "react";
//import React, { useState, useEffect, useContext } from "react";
//import cx from "classnames";
import ChatLine from "./ChatLine";
import { ChatMessage } from "elixir-backend";

interface Props {
messages: Array<string>;
messages: Array<ChatMessage>;
}

export const ChatMessagesInner: React.FC<Props> = ({ messages }) => {
return (
<>
{messages.map((m, i) => (
<div key={i}>{m}</div>
<ChatLine key={m.shortcode} message={m} />
))}
</>
);
Expand Down

0 comments on commit 0b54c16

Please sign in to comment.