aboutsummaryrefslogtreecommitdiff
path: root/shard/lib
diff options
context:
space:
mode:
Diffstat (limited to 'shard/lib')
-rw-r--r--shard/lib/app/chat.ex77
-rw-r--r--shard/lib/app/identity.ex45
-rw-r--r--shard/lib/app/pagestore.ex2
-rw-r--r--shard/lib/cli/cli.ex21
4 files changed, 125 insertions, 20 deletions
diff --git a/shard/lib/app/chat.ex b/shard/lib/app/chat.ex
index 8c55869..be3b848 100644
--- a/shard/lib/app/chat.ex
+++ b/shard/lib/app/chat.ex
@@ -31,6 +31,10 @@ defmodule SApp.Chat do
# =========
defmodule Manifest do
+ @moduledoc"""
+ Manifest for a public chat room defined by its name.
+ """
+
defstruct [:channel]
end
@@ -42,8 +46,17 @@ defmodule SApp.Chat do
defmodule PrivChat.Manifest do
+ @moduledoc"""
+ Manifest for a private chat room defined by the list of participants.
+
+ Do not instanciate this struct directly, use `new` to ensure a canonical representation.
+ """
+
defstruct [:pk_list]
+ @doc"""
+ Ensures a canonical representation by sorting pks and removing duplicates.
+ """
def new(pk_list) do
%__MODULE__{pk_list: pk_list |> Enum.sort |> Enum.uniq}
end
@@ -322,7 +335,7 @@ defmodule SApp.Chat do
end
end
- def msg_cmp({pk1, msgbin1, _sign1}, {pk2, msgbin2, _sign2}) do
+ defp msg_cmp({pk1, msgbin1, _sign1}, {pk2, msgbin2, _sign2}) do
{ts1, msg1} = SData.term_unbin msgbin1
{ts2, msg2} = SData.term_unbin msgbin2
cond do
@@ -335,4 +348,66 @@ defmodule SApp.Chat do
true -> :duplicate
end
end
+
+ # ================
+ # PUBLIC INTERFACE
+ # ================
+
+ @doc"""
+ Subscribe to notifications for this chat room.
+
+ The process calling this function will start recieving messages of the form:
+
+ {:chat_recv, manifest, {pk, msgbin, sign}}
+
+ or
+
+ {:chat_send, manifest, {pk, msgbin, sign}}
+
+ msgbin can be used in the following way:
+
+ {timestamp, message} = SData.term_unbin msgbin
+ """
+ def subscribe(shard_pid) do
+ GenServer.cast(shard_pid, {:subscribe, self()})
+ end
+
+ @doc"""
+ Send a message to a chat room.
+ """
+ def chat_send(shard_pid, pk, msg) do
+ GenServer.cast(shard_pid, {:chat_send, pk, msg})
+ end
+
+ @doc"""
+ Read the history of a chat room.
+
+ The second argument is the last message to read.
+ If nil, will read the n last messages.
+ If not nill, will read the n last messages until the specified bound.
+ """
+ def read_history(shard_pid, bound, n) do
+ GenServer.call(shard_pid, {:read_history, bound, n})
+ end
+
+ @doc"""
+ Return a shard's manifest from its pid.
+ """
+ def get_manifest(shard_pid) do
+ GenServer.call(shard_pid, :manifest)
+ end
+
+ @doc"""
+ Returns timestamp of last message if chat room has unread messages, nil otherwise.
+ """
+ def has_unread?(shard_pid) do
+ GenServer.call(shard_pid, :has_unread)
+ end
+
+ @doc"""
+ Mark all messages as read
+ """
+ def mark_read(shard_pid) do
+ GenServer.cast(shard_pid, :mark_read)
+ end
end
diff --git a/shard/lib/app/identity.ex b/shard/lib/app/identity.ex
index 95ffb92..42d1bf8 100644
--- a/shard/lib/app/identity.ex
+++ b/shard/lib/app/identity.ex
@@ -1,14 +1,29 @@
defmodule SApp.Identity do
+ @moduledoc"""
+ Shard application for keeping state associated with a user's identity.
+
+ Current functionality:
+
+ - nickname
+ - peer info: ip, port to connect to if we want a secure connection with this person
+ (used for private chat)
+
+ Future functionnality:
+
+ - friend list
+ - notifications & invites
+ """
+
use GenServer
require Logger
defmodule Manifest do
- defstruct [:pk]
- end
+ @moduledoc"""
+ Manifest for a user identity shard, defined by the public key of the user.
+ """
- defmodule State do
- defstruct [:info, :rev, :signed]
+ defstruct [:pk]
end
defimpl Shard.Manifest, for: Manifest do
@@ -17,6 +32,10 @@ defmodule SApp.Identity do
end
end
+ defmodule State do
+ defstruct [:info, :rev, :signed]
+ end
+
def start_link(pk) do
GenServer.start_link(__MODULE__, pk)
end
@@ -139,11 +158,23 @@ defmodule SApp.Identity do
end
@doc"""
+ Get the info dict of an identity shard. The pid of the shard must be given as an argument.
+ """
+ def get_info(pid) do
+ GenServer.call(pid, :get_info)
+ end
+
+ @doc"""
+ Set the info dict of an identity shard.
+ """
+ def set_info(pid, new_info) do
+ GenServer.call(pid, {:set_info, new_info})
+ end
+
+ @doc"""
Get a user's nickname from his pk
"""
def get_nick(pk) do
- pid = find_proc pk
- info = GenServer.call(pid, :get_info)
- info.nick
+ get_info(find_proc pk).nick
end
end
diff --git a/shard/lib/app/pagestore.ex b/shard/lib/app/pagestore.ex
index 86b0726..8f6be59 100644
--- a/shard/lib/app/pagestore.ex
+++ b/shard/lib/app/pagestore.ex
@@ -251,7 +251,7 @@ defmodule SApp.PageStore do
end
def ask_random_peers(state, key) do
- SNet.Group.broadcast(state.netgroup, {state.shard_id, state.path, {:get, key}}, 3)
+ SNet.Group.broadcast(state.netgroup, {state.shard_id, state.path, {:get, key}}, nmax: 3)
end
defimpl SData.PageStore do
diff --git a/shard/lib/cli/cli.ex b/shard/lib/cli/cli.ex
index 319f96b..45eeece 100644
--- a/shard/lib/cli/cli.ex
+++ b/shard/lib/cli/cli.ex
@@ -9,7 +9,7 @@ defmodule SCLI do
def run() do
for {_chid, %SApp.Chat.Manifest{}, chpid} <- Shard.Manager.list_shards do
- GenServer.cast(chpid, {:subscribe, self()})
+ SApp.Chat.subscribe(chpid)
end
pk = Shard.Keys.get_any_identity
@@ -28,14 +28,13 @@ defmodule SCLI do
nick = case id_pid do
nil -> SApp.Identity.default_nick(state.pk)
_ ->
- info = GenServer.call(id_pid, :get_info)
- info.nick
+ SApp.Identity.get_info(id_pid).nick
end
prompt = case state.room_pid do
nil -> "(no channel) #{nick}: "
_ ->
- case GenServer.call(state.room_pid, :manifest) do
+ case SApp.Chat.get_manifest(state.room_pid) do
%SApp.Chat.Manifest{channel: chan} ->
"##{chan} #{nick}: "
%SApp.Chat.PrivChat.Manifest{pk_list: pk_list} ->
@@ -57,7 +56,7 @@ defmodule SCLI do
run(state)
true ->
if str != "" do
- GenServer.cast(state.room_pid, {:chat_send, state.pk, str})
+ SApp.Chat.chat_send(state.room_pid, state.pk, str)
end
run(state)
end
@@ -102,7 +101,7 @@ defmodule SCLI do
if state.room_pid == nil do
IO.puts "Not currently on a channel!"
else
- GenServer.call(state.room_pid, {:read_history, nil, 25})
+ SApp.Chat.read_history(state.room_pid, nil, 25)
|> Enum.each(fn {{pk, msgbin, _sign}, true} ->
{ts, msg} = SData.term_unbin msgbin
nick = SApp.Identity.get_nick pk
@@ -114,14 +113,14 @@ defmodule SCLI do
defp handle_command(state, ["join", qchan]) do
pid = Shard.Manager.find_or_start %SApp.Chat.Manifest{channel: qchan}
- GenServer.cast(pid, {:subscribe, self()})
+ SApp.Chat.subscribe(pid)
IO.puts "Switching to ##{qchan}"
%{state | room_pid: pid}
end
defp handle_command(state, ["pm" | people_list]) do
known_people = for {_, %SApp.Identity.Manifest{pk: pk}, pid} <- Shard.Manager.list_shards() do
- info = GenServer.call(pid, :get_info)
+ info = SApp.Identity.get_info(pid)
{pk, info.nick}
end
pk_list = for qname <- people_list do
@@ -145,7 +144,7 @@ defmodule SCLI do
if Enum.all?(pk_list, &(&1 != :error)) do
manifest = SApp.Chat.PrivChat.Manifest.new([state.pk | pk_list])
pid = Shard.Manager.find_or_start manifest
- GenServer.cast(pid, {:subscribe, self()})
+ SApp.Chat.subscribe(pid)
IO.puts "Switching to private conversation."
%{state | room_pid: pid}
else
@@ -161,8 +160,8 @@ defmodule SCLI do
if pid == nil do
IO.puts "Sorry, we have a problem with the identity shard"
else
- info = GenServer.call(pid, :get_info)
- GenServer.call(pid, {:set_info, %{info | nick: nick}})
+ info = SApp.Identity.get_info(pid)
+ SApp.Identity.set_info(pid, %{info | nick: nick})
end
state
end