Compare commits

..

No commits in common. "fd5bcbfc05c0322632d5d15581cfbed2ed124ef8" and "2f21ccd9dea38666d8d685a8da81f0d502d82d47" have entirely different histories.

10 changed files with 76 additions and 273 deletions

View File

@ -1,7 +1,6 @@
defmodule Friends.Event do defmodule Friends.Event do
use Ecto.Schema use Ecto.Schema
import Ecto.Query alias Friends.{Relationship,Event}
alias Friends.{Relationship, Event}
alias Places.Place alias Places.Place
@repo Friends.Repo @repo Friends.Repo
@ -17,157 +16,33 @@ defmodule Friends.Event do
belongs_to(:relationship, Relationship) belongs_to(:relationship, Relationship)
end end
def changeset(event, params \\ %{}) do
event
|> Ecto.Changeset.cast(params, [
:name,
:date,
:story,
:defining,
:solo,
:place_id,
:relationship_id
])
|> Ecto.Changeset.validate_required([:name, :date, :solo, :relationship_id],
message: "This field is required."
)
|> validate_date_in_past()
|> validate_unique_event()
end
defp validate_unique_event(changeset) do
changeset
end
defp validate_date_in_past(%{changes: %{date: date}} = changeset) do
today = DateTime.utc_now() |> DateTime.to_date()
case date |> Date.diff(today) do
age when age < 0 ->
changeset |> Ecto.Changeset.add_error(:born, "Please enter a date in the past.")
_ ->
changeset
end
end
defp validate_date_in_past(changeset), do: changeset
def new(params \\ %{}) do
%Event{id: nil}
|> struct(params)
end
def get_by_id(id) do
@repo.one(
from(e in Event,
where: e.id == ^id,
preload: [:relationship, :place]
)
)
end
def get(%{relationship_id: rel, date: date, name: name}) do
@repo.one(
from(e in Event,
where: e.relationship_id == ^rel and e.date == ^date and e.name == ^name,
preload: [:relationship, :place]
)
)
end
def get_or_create(params) do
case get(params) do
nil -> create(params) |> commit()
event -> event
end
end
def create(params \\ %{id: nil}) do
Event.new(params)
|> Event.changeset()
|> Map.put(:action, :insert)
end
def update(params) do
Event.get_by_id(params.id |> String.to_integer())
|> Event.changeset(params)
|> Map.put(:action, :update)
end
def commit(changeset) do
changeset
|> @repo.commit!
|> load_preloads
end
def load_preloads(
%Event{
place: %Ecto.Association.NotLoaded{},
relationship: %Ecto.Association.NotLoaded{}
} = model
) do
model
|> @repo.preload([:place, :relationship])
end
def load_preloads(event), do: event
def create_or_update(params) do
case params.id do
"new" ->
params
|> create()
|> commit()
_number ->
params
|> update()
|> commit()
end
end
def meet(friend1, friend2, opts \\ nil) do def meet(friend1, friend2, opts \\ nil) do
relationship = Relationship.get_or_new(friend1, friend2) relationship = Relationship.get_or_new(friend1, friend2)
opts = opts ++ [if opts[:place] do
opts = {
opts ++ :place_id,
[ Places.Place.get_or_new(opts[:place]).id
if opts[:place] do
{
:place_id,
Places.Place.get_or_new(opts[:place]).id
}
end
]
{:ok, event} =
%Event{
story: opts[:story],
date: opts[:date],
place_id: opts[:place_id],
relationship_id: relationship.id
} }
|> @repo.insert end]
{:ok, event} = %Event{
story: opts[:story],
date: opts[:date],
place_id: opts[:place_id],
relationship_id: relationship.id
} |> @repo.insert
event event
end end
def person(event) do
Friends.Friend.get_by_id(event.relationship.friend_id)
end
def people(event) do def people(event) do
if event.solo, do: event.person, else: event.relationship |> Relationship.members() event.relationship |> Relationship.members
end end
def age(event) do def age(event) do
years = years = Date.diff(Date.utc_today, event.date)
Date.diff(Date.utc_today(), event.date) |> div(365)
|> div(365)
if years == 0 do if years == 0 do
months = Date.diff(Date.utc_today(), event.date) |> rem(365) |> div(12) months = Date.diff(Date.utc_today, event.date)
|> rem(365) |> div(12)
{months, :month} {months, :month}
else else
{years, :year} {years, :year}
@ -177,4 +52,5 @@ defmodule Friends.Event do
def print_age(age) do def print_age(age) do
Friends.Helpers.pluralize(age |> elem(0), age |> elem(1)) Friends.Helpers.pluralize(age |> elem(0), age |> elem(1))
end end
end end

View File

@ -1,7 +1,7 @@
defmodule Friends.Friend do defmodule Friends.Friend do
use Ecto.Schema use Ecto.Schema
alias Friends.{Relationship, Friend, Event} alias Friends.{Relationship, Friend}
import Helpers import Helpers
import Ecto.Query import Ecto.Query
@ -46,9 +46,7 @@ defmodule Friends.Friend do
:user_id, :user_id,
:address_id :address_id
]) ])
|> Ecto.Changeset.validate_required([:name, :email, :phone, :born], |> Ecto.Changeset.validate_required([:name, :email, :phone, :born], message: "This field is required.")
message: "This field is required."
)
|> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/, message: "Please enter your full name.") |> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/, message: "Please enter your full name.")
|> Ecto.Changeset.validate_format( |> Ecto.Changeset.validate_format(
:email, :email,
@ -66,20 +64,15 @@ defmodule Friends.Friend do
end end
defp validate_birthdate(%{changes: %{born: born}} = changeset) do defp validate_birthdate(%{changes: %{born: born}} = changeset) do
today = DateTime.utc_now() |> DateTime.to_date() today = DateTime.utc_now |> DateTime.to_date
case born |> Date.diff(today) |> div(-365) do case born |> Date.diff(today) |> div(-365) do
age when age < 0 -> age when age < 0 ->
changeset |> Ecto.Changeset.add_error(:born, "Please enter a date in the past.") changeset |> Ecto.Changeset.add_error(:born, "Please enter a date in the past.")
age when age > 90 -> age when age > 90 ->
changeset |> Ecto.Changeset.add_error(:born, "Are you sure you're #{age} years old?") changeset |> Ecto.Changeset.add_error(:born, "Are you sure you're #{age} years old?")
_ -> changeset
_ ->
changeset
end end
end end
defp validate_birthdate(changeset), do: changeset defp validate_birthdate(changeset), do: changeset
def all() do def all() do
@ -159,8 +152,7 @@ defmodule Friends.Friend do
"new" -> "new" ->
params params
|> create() |> create()
|> generate_slug() |> generate_slug
|> create_birth_event()
|> commit() |> commit()
_number -> _number ->
@ -170,17 +162,17 @@ defmodule Friends.Friend do
end end
end end
def get_relationships(friend, include_self \\ false) do def get_relationships(friend) do
friend friend
|> relations(include_self) |> relations
|> Enum.map(&relation(friend, &1)) |> Enum.map(&relation(friend, &1))
end end
def get_events(friend) do def get_events(friend) do
friend friend
|> get_relationships(:all) |> get_relationships
|> Enum.flat_map(& &1.events) |> Enum.map(& &1.events)
|> Enum.dedup() |> List.flatten()
end end
def age(friend) do def age(friend) do
@ -258,16 +250,4 @@ defmodule Friends.Friend do
{nil, nil} {nil, nil}
end end
end end
def create_birth_event(%Friend{id: id} = friend) do
solo_relationship = Relationship.get_or_new(friend, friend)
Event.get_or_create(%{
name: "Born",
date: friend.born,
solo: true,
defining: true,
relationship_id: solo_relationship.id
})
end
end end

View File

@ -26,17 +26,8 @@ defmodule Friends.Places.Place do
end end
def get_or_create(place) do def get_or_create(place) do
case @repo.one( place
from( |> Friends.Places.Place.validate()
p in Friends.Places.Place, |> Friends.Repo.insert!()
where: p.name == ^place.name
)
) do
nil ->
place
|> Friends.Places.Place.validate()
|> @repo.insert!()
found -> found
end
end end
end end

View File

@ -1,10 +1,9 @@
defmodule Friends.Relationship do defmodule Friends.Relationship do
use Ecto.Schema use Ecto.Schema
import Ecto.Query import Ecto.Query
alias Friends.{Relationship, Friend} alias Friends.{Relationship,Friend}
@repo Friends.Repo @repo Friends.Repo
@default_type 3
schema "relationships" do schema "relationships" do
field(:friend_id, :id) field(:friend_id, :id)
@ -23,7 +22,6 @@ defmodule Friends.Relationship do
def types do def types do
# Tuple: name of the type, associated color, and what that person "is" to the other # Tuple: name of the type, associated color, and what that person "is" to the other
{ {
{:self, :hidden, :self},
{:acquaintances, :info, nil}, {:acquaintances, :info, nil},
{:family, :primary, :relative}, {:family, :primary, :relative},
{:friends, :secondary, :friend}, {:friends, :secondary, :friend},
@ -70,61 +68,56 @@ defmodule Friends.Relationship do
) )
end end
def all() do def all() do
preloads = [] preloads = []
@repo.all(from(r in Friends.Relationship, where: r.type != 0, preload: ^preloads)) @repo.all(from(r in Friends.Relationship, preload: ^preloads))
end end
def new(friend1, friend2, type \\ @default_type) do def new(friend1, friend2, type \\ 2) do
id1 = friend1.id id1 = friend1.id
id2 = friend2.id id2 = friend2.id
{:ok, relationship} = @repo.insert(
rel_type = if id1 == id2, do: 0, else: type %Relationship{
relationship =
@repo.insert(%Relationship{
friend_id: id1, friend_id: id1,
relation_id: id2, relation_id: id2,
type: rel_type type: type
}) }
)
relationship relationship
end end
def get(friend1, friend2) do def get(friend1, friend2) do
id1 = friend1.id id1 = friend1.id
id2 = friend2.id id2 = friend2.id
rel = @repo.one(
rel = from(r in Relationship,
@repo.one( where: r.friend_id == ^id1 and r.relation_id == ^id2,
from(r in Relationship, preload: [:events]
where: r.friend_id == ^id1 and r.relation_id == ^id2,
preload: [:events]
)
) )
)
if rel == nil do if rel == nil do
@repo.one( @repo.one(
from(r in Relationship, from(r in Relationship,
where: r.friend_id == ^id2 and r.relation_id == ^id1, where: r.friend_id == ^id2 and r.relation_id == ^id1,
preload: [:events] preload: [:events]
) )
) )
else else
rel rel
end end
end end
def get_or_create(a, b) do def get_or_new(a,b) do
case get(a, b) do case get(a, b) do
nil -> new(a, b) nil -> new(a,b)
relationship -> relationship relationship -> relationship
end end
end end
def get_by_slugs([slug1, slug2]) do def get_by_slugs([slug1, slug2]) do
friend1 = slug1 |> Friend.get_by_slug() friend1 = slug1 |> Friend.get_by_slug
friend2 = slug2 |> Friend.get_by_slug() friend2 = slug2 |> Friend.get_by_slug
get(friend1, friend2) get(friend1, friend2)
end end
@ -138,33 +131,22 @@ defmodule Friends.Relationship do
def age(relationship) do def age(relationship) do
relationship.events relationship.events
|> Enum.map(fn event -> |> Enum.map(fn(event) ->
Date.diff(Date.utc_today(), event.date) Date.diff(Date.utc_today, event.date)
end) end) |> Enum.sort |> List.last |> div(365)
|> Enum.sort()
|> List.last()
|> div(365)
end end
def load_events( def load_events(%Relationship{
%Relationship{ events: %Ecto.Association.NotLoaded{}} = model) do
events: %Ecto.Association.NotLoaded{}
} = model
) do
model model
|> @repo.preload([:events]) |> @repo.preload([:events])
end end
def load_events(%Relationship{} = r), do: r def load_events(%Relationship{} = r), do: r
def load_preloads(%Relationship{
def load_preloads( events: %Ecto.Association.NotLoaded{}} = model) do
%Relationship{
events: %Ecto.Association.NotLoaded{}
} = model
) do
model model
|> @repo.preload([:events]) |> @repo.preload([:events])
end end
def load_preloads(%Relationship{} = r), do: r def load_preloads(%Relationship{} = r), do: r
end end

View File

@ -2,7 +2,6 @@ defmodule FriendsWeb.FriendsLive.Components do
use FriendsWeb, :live_component use FriendsWeb, :live_component
use Phoenix.HTML use Phoenix.HTML
import Helpers import Helpers
import FriendsWeb.LiveHelpers
alias Friends.Friend alias Friends.Friend
alias FriendsWeb.Components.{Autocomplete, Map} alias FriendsWeb.Components.{Autocomplete, Map}
alias Phoenix.LiveView.JS alias Phoenix.LiveView.JS
@ -90,13 +89,12 @@ defmodule FriendsWeb.FriendsLive.Components do
<%= for event <- @friend |> Friends.Friend.get_events do %> <%= for event <- @friend |> Friends.Friend.get_events do %>
<ul> <ul>
<li> <li>
<b><%= event.name %></b> | <%= event.name %>
<span><%= event.date |> format_date %></span>
</li> </li>
</ul> </ul>
<% end %> <% end %>
<%= if @friend |> Friends.Friend.get_events |> Enum.empty? do %> <%= if @friend |> Friends.Friend.get_events |> Enum.empty? do %>
<div class="italic">No events on record yet.</div> <div class="italic">None yet.</div>
<% end %> <% end %>
</div> </div>
""" """
@ -104,7 +102,7 @@ defmodule FriendsWeb.FriendsLive.Components do
def show_page(:relationships, assigns) do def show_page(:relationships, assigns) do
~H""" ~H"""
<div id="relationships" class="flex md:flex-row flex-col gap-8 p-8"> <div id="relationships" class="flex md:flex-row flex-col gap-8">
<%= for relation <- @friend |> relations do %> <%= for relation <- @friend |> relations do %>
<% relationship = relation(@friend, relation) %> <% relationship = relation(@friend, relation) %>
<div id={"relation-#{relation.id}"} class="card card-compact w-96 bg-base-100 shadow-xl"> <div id={"relation-#{relation.id}"} class="card card-compact w-96 bg-base-100 shadow-xl">
@ -121,7 +119,7 @@ defmodule FriendsWeb.FriendsLive.Components do
</div> </div>
<% end %> <% end %>
<%= if @friend |> relations |> Enum.empty? do %> <%= if @friend |> relations |> Enum.empty? do %>
<div class="italic">No relationships on record yet.</div> <div class="italic p-4">No relationships on record yet.</div>
<% end %> <% end %>
</div> </div>
""" """

View File

@ -1,5 +1,7 @@
defmodule FriendsWeb.FriendsLive.Friend do defmodule FriendsWeb.FriendsLive.Friend do
use FriendsWeb, :live_view use FriendsWeb, :live_view
alias FriendsWeb.FriendsLive.Components
alias FriendsWeb.Router.Helpers, as: Routes alias FriendsWeb.Router.Helpers, as: Routes
alias Friends.Friend alias Friends.Friend

View File

@ -22,8 +22,7 @@ defmodule FriendsWeb.FriendsLive.Show do
|> assign(:latlon, latlon |> Poison.encode!()) |> assign(:latlon, latlon |> Poison.encode!())
|> title(friend.name <> " - " <> (live_action |> titlecase)) |> title(friend.name <> " - " <> (live_action |> titlecase))
|> assign(:changeset, %Friend{} |> Friend.changeset()) |> assign(:changeset, %Friend{} |> Friend.changeset())
|> assign(:editable, editable) |> assign(:action, editable)}
|> assign(:action, :moot)}
else else
{:ok, socket |> redirect(to: Routes.friends_show_path(socket, :overview, friend.slug))} {:ok, socket |> redirect(to: Routes.friends_show_path(socket, :overview, friend.slug))}
end end

View File

@ -6,7 +6,7 @@
<%= if @editable do %> <%= if @editable do %>
<div class="form-control flex flex-row mb-4"> <div class="form-control flex flex-row mb-4">
<.link navigate={Routes.friends_edit_path(FriendsWeb.Endpoint, @live_action, @friend.slug)} class="btn btn-block md:btn-wide text-white"><%=@live_action |> get_edit_text%></.link> <.link navigate={Routes.friends_edit_path(FriendsWeb.Endpoint, :overview, @friend.slug)} class="btn btn-block md:btn-wide text-white">edit</.link>
</div> </div>
<% end %> <% end %>
</article> </article>

View File

@ -16,44 +16,23 @@ defmodule FriendsWeb.LiveHelpers do
display_phone(phone) display_phone(phone)
end end
end end
def display_phone(phone) do def display_phone(phone) do
""" """
TODO: Actually implement this TODO: Actually implement this
""" """
has_plus = phone |> String.starts_with?("+") has_plus = phone |> String.starts_with?("+")
case phone |> String.length do
case phone |> String.length() do
10 -> 10 ->
country = "+1 " country = "+1 "
[area, first, sec1, sec2] = phone |> to_charlist |> Enum.chunk_every(3) |> Enum.map(&(&1 |> to_string))
[area, first, sec1, sec2] =
phone |> to_charlist |> Enum.chunk_every(3) |> Enum.map(&(&1 |> to_string))
"#{country}(#{area}) #{first}-#{sec1}#{sec2}" "#{country}(#{area}) #{first}-#{sec1}#{sec2}"
IO.inspect "#{country}(#{area}) #{first}-#{sec1}#{sec2}"
11 when has_plus -> 11 when has_plus ->
phone phone
12 when has_plus ->
phone
end end
end end
def get_edit_text(live_action) do
case live_action do
:timeline -> "add a moment"
:relationships -> "add a relationship"
_ -> "edit"
end
end
def format_date(date) do
date
|> Calendar.strftime("%b %d, %Y")
end
def assign_current_user(socket, user_token) do def assign_current_user(socket, user_token) do
user = user =
case user_token do case user_token do

View File

@ -74,20 +74,16 @@ defmodule Helpers do
birthday(friend) |> Date.diff(Date.utc_today()) birthday(friend) |> Date.diff(Date.utc_today())
end end
def relations(friend, include_self \\ false) do def relations(friend) do
list = [friend.relationships, friend.reverse_relationships]
[friend.relationships, friend.reverse_relationships] |> List.flatten()
|> List.flatten()
|> Enum.dedup()
if include_self, do: list, else: list |> Enum.filter(&(&1.id != friend.id))
end end
def relation(friend, friend2) do def relation(friend, friend2) do
Friends.Relationship.get(friend, friend2) Friends.Relationship.get(friend, friend2)
end end
def events(%Friends.Relationship{} = relationship) do def events(relationship) do
relationship.events relationship.events
end end
end end