Skip to content

Commit e776892

Browse files
committed
use scheduled_at instead of schedule_in
1 parent 5d553a4 commit e776892

File tree

2 files changed

+95
-7
lines changed

2 files changed

+95
-7
lines changed

lib/algora/shared/util.ex

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,4 +203,41 @@ defmodule Algora.Util do
203203

204204
String.contains?(s1, s2) or String.contains?(s2, s1)
205205
end
206+
207+
def next_occurrence_of_time(datetime) do
208+
now = DateTime.utc_now()
209+
210+
if DateTime.after?(datetime, now) do
211+
datetime
212+
else
213+
%{hour: hour, minute: minute, second: second, microsecond: microsecond} = datetime
214+
215+
now
216+
|> DateTime.truncate(:second)
217+
|> Map.put(:hour, hour)
218+
|> Map.put(:minute, minute)
219+
|> Map.put(:second, second)
220+
|> Map.put(:microsecond, microsecond)
221+
|> then(fn target_time ->
222+
if DateTime.after?(target_time, now) do
223+
target_time
224+
else
225+
DateTime.add(target_time, 24 * 60 * 60, :second)
226+
end
227+
end)
228+
end
229+
end
230+
231+
def random_datetime(opts \\ []) do
232+
now = DateTime.utc_now()
233+
from = Keyword.get(opts, :from, DateTime.add(now, -365, :day))
234+
to = Keyword.get(opts, :to, now)
235+
236+
from_unix = DateTime.to_unix(from)
237+
to_unix = DateTime.to_unix(to)
238+
239+
from_unix..to_unix
240+
|> Enum.random()
241+
|> DateTime.from_unix!()
242+
end
206243
end

lib/algora_web/live/admin/campaign_live.ex

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ defmodule AlgoraWeb.Admin.CampaignLive do
66
import Ecto.Changeset
77
import Ecto.Query
88

9+
alias Algora.Accounts
910
alias Algora.Activities.Jobs.SendCampaignEmail
1011
alias Algora.Admin
1112
alias Algora.Mailer
1213
alias Algora.Repo
1314
alias Algora.Settings
15+
alias Algora.Util
1416
alias Algora.Workspace
1517
alias Algora.Workspace.Repository
1618
alias AlgoraWeb.LocalStore
@@ -45,11 +47,15 @@ defmodule AlgoraWeb.Admin.CampaignLive do
4547

4648
@impl true
4749
def mount(_params, _session, socket) do
50+
timezone = if(params = get_connect_params(socket), do: params["timezone"])
51+
4852
{:ok,
4953
socket
54+
|> assign(:timezone, timezone)
5055
|> assign(:page_title, "Campaign")
5156
|> assign(:form, to_form(Campaign.changeset(%Campaign{})))
5257
|> assign(:repo_cache, %{})
58+
|> assign(:user_cache, %{})
5359
|> assign_preview()}
5460
end
5561

@@ -191,7 +197,7 @@ defmodule AlgoraWeb.Admin.CampaignLive do
191197
:for={key <- @csv_columns |> Enum.filter(&(&1 != "repo_url"))}
192198
label={key}
193199
>
194-
<.cell value={row[key]} />
200+
<.cell value={row[key]} timezone={@timezone} />
195201
</:col>
196202
<:action :let={row}>
197203
<.button type="button" phx-click="send_email" phx-value-email={row["email"]}>
@@ -217,6 +223,17 @@ defmodule AlgoraWeb.Admin.CampaignLive do
217223
"""
218224
end
219225

226+
defp cell(%{value: %DateTime{}} = assigns) do
227+
~H"""
228+
<span :if={@timezone} class="tabular-nums whitespace-nowrap text-sm">
229+
{Calendar.strftime(
230+
DateTime.from_naive!(@value, "Etc/UTC") |> DateTime.shift_zone!(@timezone),
231+
"%Y/%m/%d, %H:%M:%S"
232+
)}
233+
</span>
234+
"""
235+
end
236+
220237
defp cell(assigns) do
221238
~H"""
222239
<span class="text-sm">
@@ -229,7 +246,7 @@ defmodule AlgoraWeb.Admin.CampaignLive do
229246
Enum.reduce(data, template, fn {key, value}, acc ->
230247
case value do
231248
value when is_list(value) -> acc
232-
_ -> String.replace(acc, "%{#{key}}", value)
249+
_ -> String.replace(acc, "%{#{key}}", to_string(value))
233250
end
234251
end)
235252
end
@@ -249,6 +266,39 @@ defmodule AlgoraWeb.Admin.CampaignLive do
249266

250267
defp repo_key(_row), do: nil
251268

269+
defp assign_timestamps(socket) do
270+
new_keys =
271+
socket.assigns.csv_data
272+
|> Enum.map(&Map.get(&1, "email"))
273+
|> Enum.uniq()
274+
275+
new_cache =
276+
Map.new(new_keys, fn key ->
277+
user = Accounts.get_user_by_email(key)
278+
279+
if user do
280+
{key, Util.next_occurrence_of_time(user.last_active_at || user.inserted_at)}
281+
else
282+
{key, Util.next_occurrence_of_time(Util.random_datetime())}
283+
end
284+
end)
285+
286+
updated_cache = Map.merge(socket.assigns.user_cache, new_cache)
287+
288+
csv_data =
289+
Enum.map(socket.assigns.csv_data, fn row -> Map.put(row, "timestamp", Map.get(updated_cache, row["email"])) end)
290+
291+
csv_columns =
292+
csv_data
293+
|> Enum.flat_map(&Map.keys/1)
294+
|> Enum.uniq()
295+
296+
socket
297+
|> assign(:repo_cache, updated_cache)
298+
|> assign(:csv_data, csv_data)
299+
|> assign(:csv_columns, csv_columns)
300+
end
301+
252302
defp assign_repo_names(socket) do
253303
new_keys =
254304
socket.assigns.csv_data
@@ -342,6 +392,7 @@ defmodule AlgoraWeb.Admin.CampaignLive do
342392
socket
343393
|> assign(:csv_data, csv_data)
344394
|> assign_repo_names()
395+
|> assign_timestamps()
345396
end
346397

347398
defp assign_preview(socket) do
@@ -374,16 +425,16 @@ defmodule AlgoraWeb.Admin.CampaignLive do
374425
id: Algora.Settings.get("email_campaign")["value"],
375426
subject: subject,
376427
recipient_email: recipient["email"],
377-
recipient: Algora.Util.term_to_base64(recipient),
428+
recipient: Util.term_to_base64(recipient),
378429
template: template,
379430
from_name: from_name,
380431
from_email: from_email,
381-
preheader: render_preview(preheader, recipient)
432+
preheader: render_preview(preheader, recipient),
433+
scheduled_at: recipient["timestamp"]
382434
}
383435
end)
384-
|> Enum.with_index()
385-
|> Enum.reduce_while(:ok, fn {args, index}, acc ->
386-
case args |> SendCampaignEmail.new(schedule_in: 5 * index) |> Oban.insert() do
436+
|> Enum.reduce_while(:ok, fn args, acc ->
437+
case args |> SendCampaignEmail.new(scheduled_at: args[:scheduled_at]) |> Oban.insert() do
387438
{:ok, _} -> {:cont, acc}
388439
{:error, _} -> {:halt, :error}
389440
end

0 commit comments

Comments
 (0)