diff options
-rw-r--r-- | shard/lib/app/chat.ex | 39 | ||||
-rw-r--r-- | shardweb/assets/css/app.css | 5 | ||||
-rw-r--r-- | shardweb/lib/channels/chat_channel.ex | 3 | ||||
-rw-r--r-- | shardweb/lib/controllers/identity_controller.ex | 14 | ||||
-rw-r--r-- | shardweb/lib/templates/chat/chat.html.eex | 6 | ||||
-rw-r--r-- | shardweb/lib/templates/identity/list.html.eex | 5 | ||||
-rw-r--r-- | shardweb/lib/templates/identity/view.html.eex | 25 | ||||
-rw-r--r-- | shardweb/lib/templates/layout/app.html.eex | 101 | ||||
-rw-r--r-- | shardweb/lib/templates/page/peer_list.html.eex | 7 | ||||
-rw-r--r-- | shardweb/lib/views/layout_view.ex | 21 |
10 files changed, 157 insertions, 69 deletions
diff --git a/shard/lib/app/chat.ex b/shard/lib/app/chat.ex index d8ba5da..8d55cda 100644 --- a/shard/lib/app/chat.ex +++ b/shard/lib/app/chat.ex @@ -82,7 +82,10 @@ defmodule SApp.Chat do end Shard.Manager.dispatch_to(id, nil, self()) {:ok, page_store} = SApp.PageStore.start_link(id, :page_store, netgroup) - root = Shard.Manager.load_state id + {root, read} = case Shard.Manager.load_state id do + %{root: root, read: read} -> {root, read} + _ -> {nil, nil} + end root = cond do root == nil -> nil GenServer.call(page_store, {:have_rec, root}) -> root @@ -101,6 +104,7 @@ defmodule SApp.Chat do page_store: page_store, mst: mst, subs: MapSet.new, + read: read, } } :redundant -> @@ -120,6 +124,26 @@ defmodule SApp.Chat do {:reply, ret, state} end + def handle_call(:has_unread, _from, state) do + if state.mst.root != state.read do + case MST.last(state.mst, nil, 1) do + [{{_, msgbin, _}, true}] -> + {ts, _} = SData.term_unbin msgbin + {:reply, ts, state} + [] -> + {:reply, nil, state} + end + else + {:reply, nil, state} + end + end + + def handle_cast(:mark_read, state) do + state = %{state | read: state.mst.root} + save_state(state) + {:noreply, state} + end + @doc """ Implementation of the :chat_send handler. This is the main handler that is used to send a message to the chat room. Puts the message in the store and syncs @@ -133,7 +157,7 @@ defmodule SApp.Chat do prev_root = state.mst.root mst = MST.insert(state.mst, msgitem) state = %{state | mst: mst} - Shard.Manager.save_state(state.id, mst.root) + save_state(state) for pid <- state.subs do if Process.alive?(pid) do @@ -202,7 +226,7 @@ defmodule SApp.Chat do if mst2.root == new_root do state = %{state | mst: mst2} GenServer.cast(state.page_store, {:set_roots, [mst2.root]}) - Shard.Manager.save_state(state.id, mst2.root) + save_state(state) msg_callback(state, msgitem) state else @@ -265,8 +289,9 @@ defmodule SApp.Chat do msg_callback(state, x) end GenServer.cast(state.page_store, {:set_roots, [mst.root]}) - Shard.Manager.save_state(state.id, mst.root) - %{state | mst: mst} + state = %{state | mst: mst} + save_state(state) + state else Logger.warn("Incorrect signatures somewhere while merging, dropping merged data") state @@ -279,6 +304,10 @@ defmodule SApp.Chat do {:noreply, %{ state | subs: new_subs }} end + defp save_state(state) do + Shard.Manager.save_state(state.id, %{root: state.mst.root, read: state.read}) + end + defp msg_callback(state, {pk, msgbin, sign}) do for pid <- state.subs do if Process.alive?(pid) do diff --git a/shardweb/assets/css/app.css b/shardweb/assets/css/app.css index 0676867..e8d40d2 100644 --- a/shardweb/assets/css/app.css +++ b/shardweb/assets/css/app.css @@ -4,3 +4,8 @@ color: #fff; background-color: #080808; } + +.have_unread { + color: #fff; + font-weight: bold; +} diff --git a/shardweb/lib/channels/chat_channel.ex b/shardweb/lib/channels/chat_channel.ex index f898602..a413be1 100644 --- a/shardweb/lib/channels/chat_channel.ex +++ b/shardweb/lib/channels/chat_channel.ex @@ -47,6 +47,7 @@ defmodule ShardWeb.ChatChannel do message: msg, }) end) + GenServer.cast(socket.assigns.pid, :mark_read) {:noreply, socket} end @@ -57,10 +58,12 @@ defmodule ShardWeb.ChatChannel do push socket, "shout", %{"name" => nick, "pk16" => Shard.Keys.pk_display(pk), "message" => msg} + GenServer.cast(socket.assigns.pid, :mark_read) {:noreply, socket} end def handle_info({:chat_send, _, _}, socket) do + GenServer.cast(socket.assigns.pid, :mark_read) {:noreply, socket} end diff --git a/shardweb/lib/controllers/identity_controller.ex b/shardweb/lib/controllers/identity_controller.ex index 3c04fe8..dd254bb 100644 --- a/shardweb/lib/controllers/identity_controller.ex +++ b/shardweb/lib/controllers/identity_controller.ex @@ -10,7 +10,19 @@ defmodule ShardWeb.IdentityController do end def view(conn, %{"pk" => pk}) do - #TODO + {:ok, pk} = Base.decode16(pk) + shard = %SApp.Identity.Manifest{pk: pk} |> SData.term_hash + pid = Shard.Manager.find_proc shard + + if pid == nil do + render conn, ShardWeb.ErrorView, "404.html" + else + render conn, "view.html", + view_pk: pk, + view_nick: SApp.Identity.get_nick(pk), + shard: shard, + pid: pid + end end def update(conn, params) do diff --git a/shardweb/lib/templates/chat/chat.html.eex b/shardweb/lib/templates/chat/chat.html.eex index 8a43acf..45609d8 100644 --- a/shardweb/lib/templates/chat/chat.html.eex +++ b/shardweb/lib/templates/chat/chat.html.eex @@ -18,14 +18,14 @@ <i class="fa fa-comments"></i> Chat rooms </li> <li class="active"> - <i class="fa fa-comments"></i> #<%= @chan %> + <i class="fa fa-users"></i> #<%= @chan %> </li> <% else %> <li> - <i class="fa fa-user"></i> Private chat + <i class="fa fa-comments"></i> Private chat </li> <li class="active"> - <i class="fa fa-comments"></i> <%= @nicks %> + <i class="fa fa-user"></i> <%= @nicks %> </li> <% end %> </ol> diff --git a/shardweb/lib/templates/identity/list.html.eex b/shardweb/lib/templates/identity/list.html.eex index 007af3d..6437f9c 100644 --- a/shardweb/lib/templates/identity/list.html.eex +++ b/shardweb/lib/templates/identity/list.html.eex @@ -31,7 +31,10 @@ <% end %> </td> <td> - <%= if manifest.pk != @pk do %> + <a class="btn btn-xs btn-info" href="<%= identity_path(@conn, :view, manifest.pk |> Base.encode16) %>"><i class="fa fa-info"></i></a> + <%= if manifest.pk == @pk do %> + <a class="btn btn-xs btn-warning" href="<%= identity_path(@conn, :self) %>"><i class="fa fa-edit"></i> Edit</a> + <% else %> <a class="btn btn-xs btn-primary" href="<%= chat_path(@conn, :privchat, manifest.pk |> Base.encode16) %>"><i class="fa fa-comments"></i> PM</a> <% end %> </td> diff --git a/shardweb/lib/templates/identity/view.html.eex b/shardweb/lib/templates/identity/view.html.eex new file mode 100644 index 0000000..8bb8ca2 --- /dev/null +++ b/shardweb/lib/templates/identity/view.html.eex @@ -0,0 +1,25 @@ +<!-- Page Heading --> +<div class="row"> + <div class="col-lg-12"> + <h1 class="page-header"> + <%= @view_nick %> <small><%= @view_pk |> Base.encode16 %></small> + </h1> + <ol class="breadcrumb"> + <li> + <i class="fa fa-users"></i> People + </li> + <li class="active"> + <i class="fa fa-user"></i> <%= @view_nick %> + </li> + </ol> + </div> +</div> +<!-- /.row --> + +<%= render ShardWeb.LayoutView, "flashes.html", assigns %> + +<pre> + <%= inspect((GenServer.call(@pid, :get_info)), pretty: true, width: 40) %> +</pre> + + diff --git a/shardweb/lib/templates/layout/app.html.eex b/shardweb/lib/templates/layout/app.html.eex index f49c8e6..067ad57 100644 --- a/shardweb/lib/templates/layout/app.html.eex +++ b/shardweb/lib/templates/layout/app.html.eex @@ -52,79 +52,52 @@ <!-- Top Menu Items --> <ul class="nav navbar-right top-nav"> <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-envelope"></i> <b class="caret"></b></a> + <% sur = privchat_with_unread(@conn) %> + <a href="#" class="dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-envelope"></i> + <%= if sur != [] do %> + <span class="badge badge-notify"><%= Enum.count sur %></span> + <% end %> + <b class="caret"></b> + </a> <ul class="dropdown-menu message-dropdown"> + <%= for {id, %SApp.Chat.PrivChat.Manifest{pk_list: pk_list}, pid} <- sur do %> <li class="message-preview"> - <a href="#"> - <div class="media"> - <span class="pull-left"> - <img class="media-object" src="http://placehold.it/50x50" alt=""> - </span> - <div class="media-body"> - <h5 class="media-heading"><strong>John Smith</strong> - </h5> - <p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p> - <p>Lorem ipsum dolor sit amet, consectetur...</p> - </div> - </div> - </a> - </li> - <li class="message-preview"> - <a href="#"> + <a href="<%= chat_path(@conn, :privchat, str_of_pk_list(@conn, pk_list)) %>"> <div class="media"> <span class="pull-left"> <img class="media-object" src="http://placehold.it/50x50" alt=""> </span> <div class="media-body"> - <h5 class="media-heading"><strong>John Smith</strong> + <h5 class="media-heading"><strong><%= nicks_of_pk_list(@conn, pk_list) %></strong> </h5> <p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p> - <p>Lorem ipsum dolor sit amet, consectetur...</p> + <p><%= chat_shard_last_msg(pid) %></p> </div> </div> </a> </li> - <li class="message-preview"> - <a href="#"> - <div class="media"> - <span class="pull-left"> - <img class="media-object" src="http://placehold.it/50x50" alt=""> - </span> - <div class="media-body"> - <h5 class="media-heading"><strong>John Smith</strong> - </h5> - <p class="small text-muted"><i class="fa fa-clock-o"></i> Yesterday at 4:32 PM</p> - <p>Lorem ipsum dolor sit amet, consectetur...</p> - </div> - </div> - </a> - </li> - <li class="message-footer"> - <a href="#">Read All New Messages</a> - </li> + <% end %> + <li class="message-footer"> + <a href="<%= identity_path(@conn, :list) %>"><i class="fa fa-users"></i> People</a> + </li> </ul> </li> <li class="dropdown"> - <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="fa fa-bell"></i> <b class="caret"></b></a> + <% cur = chat_with_unread(@conn) %> + <a href="#" class="dropdown-toggle" data-toggle="dropdown"> + <i class="fa fa-bell"></i> + <%= if cur != [] do %> + <span class="badge badge-notify"><%= Enum.count cur %></span> + <% end %> + <b class="caret"></b> + </a> <ul class="dropdown-menu alert-dropdown"> + <%= for {_, %SApp.Chat.Manifest{channel: chan}, _} <- cur do %> <li> - <a href="#">Alert Name <span class="label label-default">Alert Badge</span></a> - </li> - <li> - <a href="#">Alert Name <span class="label label-primary">Alert Badge</span></a> - </li> - <li> - <a href="#">Alert Name <span class="label label-success">Alert Badge</span></a> - </li> - <li> - <a href="#">Alert Name <span class="label label-info">Alert Badge</span></a> - </li> - <li> - <a href="#">Alert Name <span class="label label-warning">Alert Badge</span></a> - </li> - <li> - <a href="#">Alert Name <span class="label label-danger">Alert Badge</span></a> + <a href="<%= chat_path(@conn, :chat, chan) %>">#<%= chan %></a> </li> + <% end %> <li class="divider"></li> <li> <a href="#">View All</a> @@ -161,9 +134,15 @@ <ul id="demo" class="<%= if not (@view_module == ShardWeb.ChatView and @view_template == "chat.html" and @public) do "collapse" else "" end %>"> - <%= for {id, %SApp.Chat.Manifest{channel: name}, _} <- shard_list() do %> + <%= for {id, %SApp.Chat.Manifest{channel: name}, pid} <- shard_list() do %> <li class="<%= if @shard == id do "custom_active" else "" end %>"> - <a href="<%= chat_path(@conn, :chat, name) %>">#<%= name %></a> + <a href="<%= chat_path(@conn, :chat, name) %>"> + <%= if GenServer.call(pid, :has_unread) != nil do %> + <span class="have_unread">#<%= name %></span> + <% else %> + #<%= name %> + <% end %> + </a> </li> <% end %> <li> @@ -177,9 +156,15 @@ <a href="<%= identity_path(@conn, :list) %>"><i class="fa fa-fw fa-users"></i> People<i class="fa fa-fw fa-caret-down"></i></a> <%= if (@view_module == ShardWeb.IdentityView and @view_template == "list.html") or (@view_module == ShardWeb.ChatView and @view_template == "chat.html" and not @public) do %> <ul> - <%= for {id, %SApp.Chat.PrivChat.Manifest{pk_list: pk_list}, _} <- shard_list() do %> + <%= for {id, %SApp.Chat.PrivChat.Manifest{pk_list: pk_list}, pid} <- shard_list() do %> <li class="<%= if id == @shard do "custom_active" else "" end %>"> - <a href="<%= chat_path(@conn, :privchat, str_of_pk_list(@conn, pk_list)) %>"><%= nicks_of_pk_list(@conn, pk_list) %></a> + <a href="<%= chat_path(@conn, :privchat, str_of_pk_list(@conn, pk_list)) %>"> + <%= if GenServer.call(pid, :has_unread) != nil do %> + <span class="have_unread"><%= nicks_of_pk_list(@conn, pk_list) %></span> + <% else %> + <%= nicks_of_pk_list(@conn, pk_list) %> + <% end %> + </a> </li> <% end %> </ul> diff --git a/shardweb/lib/templates/page/peer_list.html.eex b/shardweb/lib/templates/page/peer_list.html.eex index f385528..ff6479f 100644 --- a/shardweb/lib/templates/page/peer_list.html.eex +++ b/shardweb/lib/templates/page/peer_list.html.eex @@ -30,7 +30,12 @@ <td> <%= case auth do %> <% nil -> %>(anonymous) - <% %SNet.Auth{his_pk: his_pk} -> %> <%= Shard.Keys.pk_display(his_pk) %> + <% %SNet.Auth{his_pk: his_pk} -> %> + <i class="fa fa-user"></i> + <%= SApp.Identity.get_nick(his_pk) %> + <a href="<%= identity_path(@conn, :view, his_pk|>Base.encode16) %>"> + <small><%= Shard.Keys.pk_display(his_pk) %></small> + </a> <% end %> </td> <td><%= :inet_parse.ntoa(ip) %></td> diff --git a/shardweb/lib/views/layout_view.ex b/shardweb/lib/views/layout_view.ex index 317d405..990df55 100644 --- a/shardweb/lib/views/layout_view.ex +++ b/shardweb/lib/views/layout_view.ex @@ -24,4 +24,25 @@ defmodule ShardWeb.LayoutView do l -> Enum.join(l, ", ") end end + + def chat_shard_last_msg(pid) do + [{{_, msgbin, _}, true}] = GenServer.call(pid, {:read_history, nil, 1}) + {_, msg} = SData.term_unbin msgbin + msg + end + + def privchat_with_unread(conn) do + for {id, %SApp.Chat.PrivChat.Manifest{pk_list: pk_list}, pid} <- shard_list(), + conn.assigns.pk in pk_list, + unread_time = GenServer.call(pid, :has_unread), + unread_time != nil, + do: {id, %SApp.Chat.PrivChat.Manifest{pk_list: pk_list}, pid} + end + + def chat_with_unread(conn) do + for {id, %SApp.Chat.Manifest{channel: c}, pid} <- shard_list(), + unread_time = GenServer.call(pid, :has_unread), + unread_time != nil, + do: {id, %SApp.Chat.Manifest{channel: c}, pid} + end end |