summaryrefslogtreecommitdiff
path: root/lib/lsg_web
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/lsg_web.ex3
-rw-r--r--lib/lsg_web/components/component.ex40
-rw-r--r--lib/lsg_web/components/event_component.ex36
-rw-r--r--lib/lsg_web/components/message_component.ex10
-rw-r--r--lib/lsg_web/controllers/irc_auth_sse_controller.ex2
-rw-r--r--lib/lsg_web/endpoint.ex15
-rw-r--r--lib/lsg_web/live/chat_live.ex101
-rw-r--r--lib/lsg_web/live/chat_live.html.heex96
-rw-r--r--lib/lsg_web/router.ex9
-rw-r--r--lib/lsg_web/templates/irc/index.html.eex3
-rw-r--r--lib/lsg_web/templates/layout/app.html.eex24
-rw-r--r--lib/lsg_web/templates/layout/root.html.leex18
12 files changed, 324 insertions, 33 deletions
diff --git a/lib/lsg_web.ex b/lib/lsg_web.ex
index 113d00d..eb0cdc5 100644
--- a/lib/lsg_web.ex
+++ b/lib/lsg_web.ex
@@ -63,6 +63,8 @@ defmodule LSGWeb do
import LSGWeb.Router.Helpers
import LSGWeb.ErrorHelpers
import LSGWeb.Gettext
+
+ import Phoenix.LiveView.Helpers
end
end
@@ -71,6 +73,7 @@ defmodule LSGWeb do
use Phoenix.Router
import Plug.Conn
import Phoenix.Controller
+ import Phoenix.LiveView.Router
end
end
diff --git a/lib/lsg_web/components/component.ex b/lib/lsg_web/components/component.ex
new file mode 100644
index 0000000..37d75e3
--- /dev/null
+++ b/lib/lsg_web/components/component.ex
@@ -0,0 +1,40 @@
+defmodule LSGWeb.Component do
+ use Phoenix.Component
+
+ @date_time_default_format "%F %H:%M"
+ @date_time_formats %{"time-24-with-seconds" => "%H:%M:%S"}
+ def naive_date_time_utc(assigns = %{format: format}) do
+ assigns = assign(assigns, :format, Map.get(@date_time_formats, format, format))
+ ~H"""
+ <time class="component"
+ id={"time-#{:erlang.phash2(@datetime)}"}
+ phx-hook="NaiveDateTimeUTC"
+ data-time-format={get_luxon_format(@format)}
+ datetime={NaiveDateTime.to_iso8601(@datetime)}>
+ <%= Timex.format!(@datetime, @format, :strftime) %>
+ </time>
+ """
+ end
+ def naive_date_time_utc(assigns) do
+ naive_date_time_utc(assign(assigns, :format, "%F %H:%M"))
+ end
+ def get_luxon_format("%H:%M:%S"), do: "TIME_24_WITH_SECONDS"
+
+ def nick(assigns = %{self: false}) do
+ ~H"""
+ <span class="nickname" data-account-id={@account_id} data-user-id={@user_id}>
+ <%= @nick %>
+ </span>
+ """
+ end
+
+ def nick(assigns = %{self: true}) do
+ ~H"""
+ <span class="nickname self" data-account-id={@account_id} data-user-id={@user_id}>
+ You
+ </span>
+ """
+ end
+
+
+end
diff --git a/lib/lsg_web/components/event_component.ex b/lib/lsg_web/components/event_component.ex
new file mode 100644
index 0000000..3b9cd3b
--- /dev/null
+++ b/lib/lsg_web/components/event_component.ex
@@ -0,0 +1,36 @@
+defmodule LSGWeb.EventComponent do
+ use Phoenix.Component
+
+ def content(assigns = %{event: %{type: :quit}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ has quit:
+ <span class="reason"><%= @reason %></span>
+ """
+ end
+
+ def content(assigns = %{event: %{type: :part}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ has left:
+ <span class="reason"><%= @reason %></span>
+ """
+ end
+
+ def content(assigns = %{event: %{type: :nick}}) do
+ ~H"""
+ <span class="old-nick"><%= @old_nick %></span>
+ is now known as
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ """
+ end
+
+ def content(assigns = %{event: %{type: :join}}) do
+ ~H"""
+ <LSGWeb.Component.nick self={@self} nick={@user.nick} user_id={@user.id} account_id={@user.account} />
+ joined
+ """
+ end
+
+
+end
diff --git a/lib/lsg_web/components/message_component.ex b/lib/lsg_web/components/message_component.ex
new file mode 100644
index 0000000..2381411
--- /dev/null
+++ b/lib/lsg_web/components/message_component.ex
@@ -0,0 +1,10 @@
+defmodule LSGWeb.MessageComponent do
+ use Phoenix.Component
+
+ def content(assigns) do
+ ~H"""
+ <div class="inline-block flex-grow cursor-default"><%= @text %></div>
+ """
+ end
+
+end
diff --git a/lib/lsg_web/controllers/irc_auth_sse_controller.ex b/lib/lsg_web/controllers/irc_auth_sse_controller.ex
index c39a866..f370d97 100644
--- a/lib/lsg_web/controllers/irc_auth_sse_controller.ex
+++ b/lib/lsg_web/controllers/irc_auth_sse_controller.ex
@@ -26,7 +26,7 @@ defmodule LSGWeb.IrcAuthSseController do
def subscribe(conn) do
:timer.send_interval(@ping_interval, {:event, :ping})
:timer.send_after(@expire_delay, {:event, :expire})
- {:ok, _} = Registry.register(IRC.PubSub, "message:private", [])
+ {:ok, _} = Registry.register(IRC.PubSub, "messages:private", [])
conn
end
diff --git a/lib/lsg_web/endpoint.ex b/lib/lsg_web/endpoint.ex
index 37f7e84..4dd8151 100644
--- a/lib/lsg_web/endpoint.ex
+++ b/lib/lsg_web/endpoint.ex
@@ -1,8 +1,6 @@
defmodule LSGWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :lsg
- socket "/socket", LSGWeb.UserSocket, websocket: true
-
# Serve at "/" the static files from "priv/static" directory.
#
# You should set gzip to true if you are running phoenix.digest
@@ -30,13 +28,18 @@ defmodule LSGWeb.Endpoint do
plug Plug.MethodOverride
plug Plug.Head
+ @session_options [store: :cookie,
+ key: "_lsg_key",
+ signing_salt: "+p7K3wrj"]
+
+
+ socket "/live", Phoenix.LiveView.Socket,
+ websocket: [connect_info: [session: @session_options]]
+
# The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it.
- plug Plug.Session,
- store: :cookie,
- key: "_lsg_key",
- signing_salt: "+p7K3wrj"
+ plug Plug.Session, @session_options
plug LSGWeb.Router
diff --git a/lib/lsg_web/live/chat_live.ex b/lib/lsg_web/live/chat_live.ex
new file mode 100644
index 0000000..a2b4c13
--- /dev/null
+++ b/lib/lsg_web/live/chat_live.ex
@@ -0,0 +1,101 @@
+defmodule LSGWeb.ChatLive do
+ use Phoenix.LiveView
+ use Phoenix.HTML
+ require Logger
+
+ def mount(%{"network" => network, "chan" => chan}, %{"account" => account_id}, socket) do
+ chan = LSGWeb.reformat_chan(chan)
+ connection = IRC.Connection.get_network(network, chan)
+ account = IRC.Account.get(account_id)
+ membership = IRC.Membership.of_account(IRC.Account.get("DRgpD4fLf8PDJMLp8Dtb"))
+ if account && connection && Enum.member?(membership, {connection.network, chan}) do
+ {:ok, _} = Registry.register(IRC.PubSub, "#{connection.network}:events", plugin: __MODULE__)
+ for t <- ["messages", "triggers", "outputs", "events"] do
+ {:ok, _} = Registry.register(IRC.PubSub, "#{connection.network}/#{chan}:#{t}", plugin: __MODULE__)
+ end
+
+ IRC.PuppetConnection.start(account, connection)
+
+ users = IRC.UserTrack.channel(connection.network, chan)
+ |> Enum.map(fn(tuple) -> IRC.UserTrack.User.from_tuple(tuple) end)
+ |> Enum.reduce(Map.new, fn(user = %{id: id}, acc) ->
+ Map.put(acc, id, user)
+ end)
+
+ socket = socket
+ |> assign(:connection_id, connection.id)
+ |> assign(:network, connection.network)
+ |> assign(:chan, chan)
+ |> assign(:title, "live")
+ |> assign(:channel, chan)
+ |> assign(:account_id, account.id)
+ |> assign(:backlog, [])
+ |> assign(:users, users)
+ |> assign(:counter, 0)
+
+ {:ok, socket}
+ else
+ {:ok, redirect(socket, to: "/")}
+ end
+ end
+
+ def handle_event("send", %{"message" => %{"text" => text}}, socket) do
+ account = IRC.Account.get(socket.assigns.account_id)
+ IRC.send_message_as(account, socket.assigns.network, socket.assigns.channel, text, true)
+ {:noreply, assign(socket, :counter, socket.assigns.counter + 1)}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :join, user_id: id}}, socket) do
+ if user = IRC.UserTrack.lookup(id) do
+ IO.puts("JOIN USER JOIN USER JOIN USER")
+
+ socket = socket
+ |> assign(:users, Map.put(socket.assigns.users, id, user))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+
+ IO.inspect(socket.assigns.users)
+
+ {:noreply, socket}
+ else
+ IO.puts("\n\n\n?!\n\n\n?!\n\n\n\n")
+ {:noreply, socket}
+ end
+ end
+
+ def handle_info({:irc, :event, event = %{type: :nick, user_id: id, nick: nick}}, socket) do
+ socket = socket
+ |> assign(:users, update_in(socket.assigns.users, [id, :nick], nick))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :quit, user_id: id}}, socket) do
+ socket = socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :event, event = %{type: :part, user_id: id}}, socket) do
+ socket = socket
+ |> assign(:users, Map.delete(socket.assigns.users, id))
+ |> assign(:backlog, socket.assigns.backlog ++ [event])
+ {:noreply, socket}
+ end
+
+ def handle_info({:irc, :trigger, _, message}, socket) do
+ handle_info({:irc, nil, message}, socket)
+ end
+
+ def handle_info({:irc, :text, message}, socket) do
+ socket = socket
+ |> assign(:backlog, socket.assigns.backlog ++ [message])
+ {:noreply, socket}
+ end
+
+ def handle_info(info, socket) do
+ Logger.debug("Unhandled info: #{inspect info}")
+ {:noreply, socket}
+ end
+
+end
diff --git a/lib/lsg_web/live/chat_live.html.heex b/lib/lsg_web/live/chat_live.html.heex
new file mode 100644
index 0000000..01d8b3a
--- /dev/null
+++ b/lib/lsg_web/live/chat_live.html.heex
@@ -0,0 +1,96 @@
+<div class="chat" data-turbo="false">
+
+ <div class="py-4 px-4 bg-gradient-to-b from-black to-gray-900">
+ <div class="grid grid-cols-2">
+ <h1 class="text-gray-50 tracking-tight font-extrabold text-xl">
+ <%= @network %>
+ <span class="font-bold"><%= @chan %></span>
+ </h1>
+ <div class="text-right">
+ <a href="/" class="text-gray-400"><%= @account_id %></a>
+ </div>
+ </div>
+ </div>
+
+ <div class="body">
+
+ <div class="log">
+ <%= if Enum.empty?(@backlog) do %>
+ <p class="disconnected text-center text-6xl tracking-tight font-extrabold text-red-800 w-full my-24 mx-auto overflow-y-auto">
+ Disconnected
+ </p>
+ <p class="phx-errored text-center text-6xl tracking-tight font-extrabold text-red-800 w-full my-24 mx-auto overflow-y-auto">
+ Oh no error
+ </p>
+ <% end %>
+
+ <ul class="pt-4 pl-4">
+ <%= for message <- @backlog do %>
+ <%= if is_map(message) && Map.get(message, :__struct__) == IRC.Message do %>
+ <li class="flex gap-2 place-items-center message"
+ data-account-id={message.account.id}>
+ <LSGWeb.Component.naive_date_time_utc datetime={message.at} format="time-24-with-seconds" />
+ <span class="inline-block font-bold flex-none cursor-default"><%= message.sender.nick %></span>
+ <span class="inline-block flex-grow cursor-default">
+ <LSGWeb.MessageComponent.content
+ self={message.account.id == @account_id}
+ text={message.text}
+ />
+ </span>
+ </li>
+ <% end %>
+
+ <%= if is_binary(message) do %>
+ <li class="notice"><%= message %></li>
+ <% end %>
+
+ <%= if is_map(message) && Map.get(message, :type) do %>
+ <li class="flex gap-2 place-items-center event">
+ <LSGWeb.Component.naive_date_time_utc datetime={message.at} format="time-24-with-seconds" />
+ <span class="inline-block font-bold flex-none cursor-default text-gray-700">*&nbsp;*&nbsp;*</span>
+ <span class="inline-block flex-grow cursor-default text-gray-700">
+ <LSGWeb.EventComponent.content event={message}
+ self={@users[message.user_id] && @users[message.user_id].account == @account_id}
+ user={@users[message.user_id]}
+ />
+ </span>
+ </li>
+ <% end %>
+ <% end %>
+ </ul>
+ </div>
+
+ <aside>
+ <%= for {_, user} <- @users do %>
+ <details class="user dropdown">
+ <summary><%= user.nick %></summary>
+ <div class="content">
+ <h3 class="text-xl font-bold"><%= user.nick %></h3>
+
+ <ul class="mt-4 space-y-2">
+ <li class="">User: <span class="font-bold"><%= user.username %></span></li>
+ <li class="">Name: <%= user.realname || user.nick %></li>
+ <li class="">Host: <span class="font-mono"><%= user.host %></span></li>
+ </ul>
+
+ <div class="mt-4 font-xs text-gray-300 text-center">
+ UID: <%= user.id %>
+ <br />
+ AID: <%= user.account %>
+ </div>
+ </div>
+ </details>
+ <% end %>
+ </aside>
+
+ </div>
+
+ <.form let={f} id={"form-#{@counter}"} for={:message} phx-submit="send" class="w-full px-4 pt-4">
+ <div>
+ <div class="mt-1 flex rounded-md shadow-sm border border-gray-300">
+ <%= text_input f, :text, class: "focus:ring-indigo-500 focus:border-indigo-500 block w-full border rounded-md pl-4 sm:text-sm border-gray-300", autofocus: true, 'phx-hook': "AutoFocus", autocomplete: "off", placeholder: "Don't be shy, say something…" %>
+ <%= submit content_tag(:span, "Send"), class: "-ml-px relative inline-flex items-center space-x-2 px-4 py-2 border border-gray-300 text-sm font-medium rounded-r-md text-gray-700 bg-gray-50 hover:bg-gray-100 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"%>
+ </div>
+ </div>
+ </.form>
+</div>
diff --git a/lib/lsg_web/router.ex b/lib/lsg_web/router.ex
index d681ed9..5fcf0a8 100644
--- a/lib/lsg_web/router.ex
+++ b/lib/lsg_web/router.ex
@@ -5,8 +5,10 @@ defmodule LSGWeb.Router do
plug :accepts, ["html", "txt"]
plug :fetch_session
plug :fetch_flash
- #plug :protect_from_forgery
- #plug :put_secure_browser_headers
+ plug :fetch_live_flash
+ plug :protect_from_forgery
+ plug :put_secure_browser_headers
+ plug :put_root_layout, {LSGWeb.LayoutView, :root}
end
pipeline :api do
@@ -22,8 +24,6 @@ defmodule LSGWeb.Router do
scope "/", LSGWeb do
pipe_through :browser
get "/", PageController, :index
- get "/embed/widget", PageController, :widget
- get "/api", PageController, :api
get "/login/irc/:token", PageController, :token, as: :login
get "/api/untappd/callback", UntappdController, :callback, as: :untappd_callback
@@ -43,6 +43,7 @@ defmodule LSGWeb.Router do
get "/:network/:chan/alcoolog/gls.json", AlcoologController, :index_gls_json
put "/api/alcoolog/minisync/:user_id/meta/:key", AlcoologController, :minisync_put_meta
get "/:network/:chan", IrcController, :index
+ live "/:network/:chan/live", ChatLive
get "/:network/:chan/txt", IrcController, :txt
get "/:network/:chan/txt/:name", IrcController, :txt
get "/:network/:channel/preums", IrcController, :preums
diff --git a/lib/lsg_web/templates/irc/index.html.eex b/lib/lsg_web/templates/irc/index.html.eex
index a8544b3..f20f444 100644
--- a/lib/lsg_web/templates/irc/index.html.eex
+++ b/lib/lsg_web/templates/irc/index.html.eex
@@ -1,5 +1,6 @@
<div class="hidden sm:block">
- <nav class="flex flex-wrap content-center">
+ <nav class="flex flex-wrap content-center">
+ <%= link("live", to: LSGWeb.Router.Helpers.live_path(LSGWeb.Endpoint, LSGWeb.ChatLive, @network, LSGWeb.format_chan(@chan)), class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %>
<% list = for {identifier, _} <- @commands do %>
<% name = String.replace(identifier, "_", " ") %>
<%= link(name, to: "##{identifier}", class: "px-3 py-2 font-medium text-sm leading-5 rounded-md text-gray-500 hover:text-gray-700 focus:outline-none focus:text-indigo-600 focus:bg-indigo-50") %>
diff --git a/lib/lsg_web/templates/layout/app.html.eex b/lib/lsg_web/templates/layout/app.html.eex
index 9ad05a6..bca1555 100644
--- a/lib/lsg_web/templates/layout/app.html.eex
+++ b/lib/lsg_web/templates/layout/app.html.eex
@@ -1,18 +1,3 @@
-<!DOCTYPE html>
-<html lang="en">
- <head>
- <%= page_title(@conn) %>
- <meta charset="utf-8">
- <meta name="robots" content="noindex, nofollow, nosnippet">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <meta name="robots" content="noindex, noarchive, nofollow, nosnippet" />
- <title><%= Map.get(assigns, :title, "") %></title>
- <link rel="stylesheet" href="<%= static_path(@conn, "/assets/site.css") %>">
- <script src="<%= static_path(@conn, "/assets/site.js") %>" defer></script>
- </head>
-
- <body>
<div>
<div class="bg-gray-800 pb-32">
<nav class="bg-gray-800">
@@ -129,16 +114,13 @@
</header>
</div>
- <main class="-mt-32">
- <div class="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
+ <main class="-mt-32 h-full">
+ <div class="max-w-7xl h-full mx-auto pb-12 px-4 sm:px-6 lg:px-8">
<!-- Replace with your content -->
- <div class="bg-white rounded-lg shadow px-5 py-6 sm:px-6">
+ <div class="bg-white h-full rounded-lg shadow px-5 py-6 sm:px-6">
<%= @inner_content %>
</div>
<!-- /End replace -->
</div>
</main>
</div>
-
- </body>
-</html>
diff --git a/lib/lsg_web/templates/layout/root.html.leex b/lib/lsg_web/templates/layout/root.html.leex
new file mode 100644
index 0000000..6a48506
--- /dev/null
+++ b/lib/lsg_web/templates/layout/root.html.leex
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <%= page_title(@conn) %>
+ <meta charset="utf-8">
+ <meta name="robots" content="noindex, nofollow, nosnippet">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="robots" content="noindex, noarchive, nofollow, nosnippet" />
+ <title><%= Map.get(assigns, :title, "") %></title>
+ <link rel="stylesheet" href="<%= static_path(@conn, "/assets/site.css") %>">
+ <%= csrf_meta_tag() %>
+ <script src="<%= static_path(@conn, "/assets/site.js") %>" defer></script>
+ </head>
+ <body>
+ <%= @inner_content %>
+ </body>
+</html>