Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions priv/assets/css/ldf.css
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,22 @@ tbody tr:last-child td { border-bottom: none; }
}
tbody tr.new { animation: flashin .7s ease-out; }

/* intercept records (receiver feed) */
.records { display: flex; flex-direction: column; }
.rec { padding: 14px 18px; border-bottom: 1px solid var(--line-soft); }
.rec:last-child { border-bottom: none; }
.rec.new { animation: flashin .7s ease-out; }
.rec-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.rec-from { font-family: var(--sans); font-weight: 600; font-size: 13px; color: var(--text); }
.rec-phone { color: var(--amber); font-weight: 400; font-size: 12px; margin-left: 8px; }
.rec-time { margin-left: auto; color: var(--faint); font-size: 11px; white-space: nowrap; }
.rec-body { color: var(--text); font-size: 13px; margin-bottom: 10px; word-break: break-word; padding-left: 10px; border-left: 2px solid var(--line); }
.rec-body a { color: var(--live); text-decoration: none; border-bottom: 1px dotted var(--live-dim); word-break: break-all; }
.rec-body a:hover { border-bottom-style: solid; }
.rec-meta { display: flex; flex-wrap: wrap; gap: 5px 18px; font-size: 10.5px; color: var(--dim); }
.rec-meta i { color: var(--faint); font-style: normal; text-transform: uppercase; letter-spacing: .08em; margin-right: 6px; }
.rec-meta .rec-agent { flex-basis: 100%; color: var(--faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.empty { padding: 40px 18px; text-align: center; color: var(--faint); font-size: 12.5px; }
.empty::before { content: "▱ "; color: var(--live-dim); }

Expand Down
90 changes: 63 additions & 27 deletions src/controllers/ldf_www_controller.erl
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ submit_history(Req0) ->

%% --- published by the message API on each intercepted message -------------

publish_message(MessageId, Payload) ->
Row = render(message_row_dtl, row_vars(ref(MessageId), Payload, true)),
publish_message(_MessageId, Payload) ->
Row = render(message_row_dtl, row_vars(Payload, true)),
datastar_nova:publish(?MESSAGES, [
datastar:patch_elements(~"", #{selector => ~"#feed-empty", mode => remove}),
datastar:patch_elements(Row, #{selector => ~"#messages", mode => append})
Expand All @@ -91,35 +91,75 @@ publish_message(MessageId, Payload) ->
message_init([]) ->
[datastar:patch_elements(feed_empty(), #{selector => ~"#messages", mode => inner})];
message_init(Msgs) ->
Rows = [
render(
message_row_dtl,
row_vars(ref(maps:get(message_id, M, ~"")), maps:get(payload, M, ~""), false)
)
|| M <- Msgs
],
Rows = [render(message_row_dtl, row_vars(maps:get(payload, M, ~""), false)) || M <- Msgs],
[datastar:patch_elements(Rows, #{selector => ~"#messages", mode => inner})].

feed_empty() ->
~"<tr id=\"feed-empty\"><td colspan=\"2\" class=\"empty\">Awaiting interception</td></tr>".

%% The stored payload is the full encoded message; show its inner payload -
%% an attachment URL becomes a clickable link, plain text stays text.
row_vars(Ref, StoredPayload, New) ->
case display_payload(StoredPayload) of
{link, Url} -> [{ref, Ref}, {new, New}, {link, Url}, {text, ~""}];
{text, Text} -> [{ref, Ref}, {new, New}, {link, false}, {text, Text}]
~"<div id=\"feed-empty\" class=\"empty\">Awaiting interception</div>".

%% The stored payload is the full encoded message; expand every field for a
%% structured record. The inner payload renders as text, or a clickable link
%% for attachments.
row_vars(StoredPayload, New) ->
case safe_decode(StoredPayload) of
Msg when is_map(Msg) ->
[{new, New} | fields(Msg)];
_ ->
[{new, New}, {text, StoredPayload}, {link, false} | blank_fields()]
end.

display_payload(StoredPayload) ->
try json:decode(StoredPayload) of
#{~"payload" := #{~"url" := Url}} when is_binary(Url) -> {link, public_url(Url)};
#{~"payload" := Text} when is_binary(Text) -> {text, Text};
_ -> {text, StoredPayload}
fields(Msg) ->
SenderInfo = maps:get(~"sender_info", Msg, #{}),
Payload =
case maps:get(~"payload", Msg, ~"") of
#{~"url" := Url} when is_binary(Url) -> [{link, public_url(Url)}, {text, ~""}];
Text when is_binary(Text) -> [{link, false}, {text, Text}];
_ -> [{link, false}, {text, ~""}]
end,
[
{email, maps:get(~"email", SenderInfo, ~"")},
{phone, maps:get(~"phone_number", SenderInfo, ~"")},
{agent, maps:get(~"user_agent", SenderInfo, ~"")},
{time, fmt_time(maps:get(~"timestamp", Msg, undefined))},
{type, maps:get(~"type", Msg, ~"")},
{action, maps:get(~"action", Msg, ~"")},
{sender, short(maps:get(~"sender", Msg, ~""))},
{recipient, short(maps:get(~"to", Msg, ~""))},
{chat, short(maps:get(~"chat_id", Msg, ~""))},
{id, short(maps:get(~"id", Msg, ~""))}
| Payload
].

blank_fields() ->
[
{email, ~""},
{phone, ~""},
{agent, ~""},
{time, ~""},
{type, ~""},
{action, ~""},
{sender, ~""},
{recipient, ~""},
{chat, ~""},
{id, ~""}
].

safe_decode(Bin) ->
try json:decode(Bin) of
Decoded -> Decoded
catch
_:_ -> {text, StoredPayload}
_:_ -> error
end.

fmt_time(Ms) when is_integer(Ms) ->
list_to_binary(calendar:system_time_to_rfc3339(Ms div 1000, [{offset, "Z"}]));
fmt_time(_) ->
~"".

short(<<Eight:8/binary, _/binary>>) -> Eight;
short(Bin) when is_binary(Bin) -> Bin;
short(_) -> ~"".

%% Rewrite an attachment URL onto the browser-reachable chatli host,
%% regardless of which host got baked in when it was stored.
public_url(Url) ->
Expand Down Expand Up @@ -155,7 +195,3 @@ status_patch(Text) ->
render(Mod, Vars) ->
{ok, Html} = Mod:render(Vars),
string:trim(iolist_to_binary(Html), trailing).

ref(<<Short:8/binary, _/binary>>) -> Short;
ref(MessageId) when is_binary(MessageId) -> MessageId;
ref(_) -> ~"".
2 changes: 1 addition & 1 deletion src/views/message_row.dtl
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<tr{% if new %} class="new"{% endif %}><td class="mono-id">{{ ref }}</td><td class="msg">{% if link %}<a href="{{ link }}" target="_blank" rel="noopener">{{ link }}</a>{% else %}{{ text }}{% endif %}</td></tr>
<div class="rec{% if new %} new{% endif %}"><div class="rec-head"><span class="rec-from">{% if email %}{{ email }}{% endif %}{% if phone %}<span class="rec-phone">{{ phone }}</span>{% endif %}{% if not email %}{% if not phone %}<span class="rec-phone">unidentified</span>{% endif %}{% endif %}</span><span class="rec-time">{{ time }}</span></div><div class="rec-body">{% if link %}<a href="{{ link }}" target="_blank" rel="noopener">{{ link }}</a>{% else %}{{ text }}{% endif %}</div><div class="rec-meta"><span><i>type</i>{{ type }}{% if action %} / {{ action }}{% endif %}</span><span><i>msg</i>{{ id }}</span><span><i>chat</i>{{ chat }}</span><span><i>sender</i>{{ sender }}</span><span><i>to</i>{{ recipient }}</span>{% if agent %}<span class="rec-agent"><i>agent</i>{{ agent }}</span>{% endif %}</div></div>
9 changes: 3 additions & 6 deletions src/views/receiver.dtl
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,9 @@
<span class="count">SSE&nbsp;·&nbsp;/sse/receiver</span>
</div>
<div class="panel-body" style="padding:0">
<table class="feed">
<thead><tr><th>Reference</th><th>Payload</th></tr></thead>
<tbody id="messages">
<tr id="feed-empty"><td colspan="2" class="empty">Awaiting interception</td></tr>
</tbody>
</table>
<div id="messages" class="records">
<div id="feed-empty" class="empty">Awaiting interception</div>
</div>
</div>
</div>
{% endblock %}
Loading