diff --git a/friends/lib/friends/event.ex b/friends/lib/friends/event.ex index e48f7bb..73e0668 100644 --- a/friends/lib/friends/event.ex +++ b/friends/lib/friends/event.ex @@ -1,6 +1,7 @@ defmodule Friends.Event do use Ecto.Schema - alias Friends.{Relationship,Event} + import Ecto.Query + alias Friends.{Relationship, Event} alias Places.Place @repo Friends.Repo @@ -16,33 +17,157 @@ defmodule Friends.Event do belongs_to(:relationship, Relationship) 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 relationship = Relationship.get_or_new(friend1, friend2) - opts = opts ++ [if opts[:place] do - { - :place_id, - Places.Place.get_or_new(opts[:place]).id + + opts = + opts ++ + [ + 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 } - end] - {:ok, event} = %Event{ - story: opts[:story], - date: opts[:date], - place_id: opts[:place_id], - relationship_id: relationship.id - } |> @repo.insert + |> @repo.insert + event end + def person(event) do + Friends.Friend.get_by_id(event.relationship.friend_id) + end + def people(event) do - event.relationship |> Relationship.members + if event.solo, do: event.person, else: event.relationship |> Relationship.members() end def age(event) do - years = Date.diff(Date.utc_today, event.date) - |> div(365) + years = + Date.diff(Date.utc_today(), event.date) + |> div(365) + 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} else {years, :year} @@ -52,5 +177,4 @@ defmodule Friends.Event do def print_age(age) do Friends.Helpers.pluralize(age |> elem(0), age |> elem(1)) end - end diff --git a/friends/lib/friends/friend.ex b/friends/lib/friends/friend.ex index 42281e3..da7ae34 100644 --- a/friends/lib/friends/friend.ex +++ b/friends/lib/friends/friend.ex @@ -1,7 +1,7 @@ defmodule Friends.Friend do use Ecto.Schema - alias Friends.{Relationship, Friend} + alias Friends.{Relationship, Friend, Event} import Helpers import Ecto.Query @@ -46,7 +46,9 @@ defmodule Friends.Friend do :user_id, :address_id ]) - |> Ecto.Changeset.validate_required([:name, :email, :phone, :born], message: "This field is required.") + |> Ecto.Changeset.validate_required([:name, :email, :phone, :born], + message: "This field is required." + ) |> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/, message: "Please enter your full name.") |> Ecto.Changeset.validate_format( :email, @@ -64,15 +66,20 @@ defmodule Friends.Friend do end 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 age when age < 0 -> changeset |> Ecto.Changeset.add_error(:born, "Please enter a date in the past.") + age when age > 90 -> changeset |> Ecto.Changeset.add_error(:born, "Are you sure you're #{age} years old?") - _ -> changeset + + _ -> + changeset end end + defp validate_birthdate(changeset), do: changeset def all() do @@ -152,7 +159,8 @@ defmodule Friends.Friend do "new" -> params |> create() - |> generate_slug + |> generate_slug() + |> create_birth_event() |> commit() _number -> @@ -162,17 +170,17 @@ defmodule Friends.Friend do end end - def get_relationships(friend) do + def get_relationships(friend, include_self \\ false) do friend - |> relations + |> relations(include_self) |> Enum.map(&relation(friend, &1)) end def get_events(friend) do friend - |> get_relationships - |> Enum.map(& &1.events) - |> List.flatten() + |> get_relationships(:all) + |> Enum.flat_map(& &1.events) + |> Enum.dedup() end def age(friend) do @@ -250,4 +258,16 @@ defmodule Friends.Friend do {nil, nil} 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 diff --git a/friends/lib/friends/places/place.ex b/friends/lib/friends/places/place.ex index 98ea6c9..06736bf 100644 --- a/friends/lib/friends/places/place.ex +++ b/friends/lib/friends/places/place.ex @@ -26,8 +26,17 @@ defmodule Friends.Places.Place do end def get_or_create(place) do - place - |> Friends.Places.Place.validate() - |> Friends.Repo.insert!() + case @repo.one( + from( + p in Friends.Places.Place, + where: p.name == ^place.name + ) + ) do + nil -> + place + |> Friends.Places.Place.validate() + |> @repo.insert!() + found -> found + end end end diff --git a/friends/lib/friends/relationship.ex b/friends/lib/friends/relationship.ex index 11b013f..20ca34b 100644 --- a/friends/lib/friends/relationship.ex +++ b/friends/lib/friends/relationship.ex @@ -1,9 +1,10 @@ defmodule Friends.Relationship do use Ecto.Schema import Ecto.Query - alias Friends.{Relationship,Friend} + alias Friends.{Relationship, Friend} @repo Friends.Repo + @default_type 3 schema "relationships" do field(:friend_id, :id) @@ -22,6 +23,7 @@ defmodule Friends.Relationship do def types do # Tuple: name of the type, associated color, and what that person "is" to the other { + {:self, :hidden, :self}, {:acquaintances, :info, nil}, {:family, :primary, :relative}, {:friends, :secondary, :friend}, @@ -68,56 +70,61 @@ defmodule Friends.Relationship do ) end - def all() do preloads = [] - @repo.all(from(r in Friends.Relationship, preload: ^preloads)) + @repo.all(from(r in Friends.Relationship, where: r.type != 0, preload: ^preloads)) end - def new(friend1, friend2, type \\ 2) do + def new(friend1, friend2, type \\ @default_type) do id1 = friend1.id id2 = friend2.id - {:ok, relationship} = @repo.insert( - %Relationship{ + + rel_type = if id1 == id2, do: 0, else: type + + relationship = + @repo.insert(%Relationship{ friend_id: id1, relation_id: id2, - type: type - } - ) + type: rel_type + }) + relationship end def get(friend1, friend2) do id1 = friend1.id id2 = friend2.id - rel = @repo.one( - from(r in Relationship, - where: r.friend_id == ^id1 and r.relation_id == ^id2, - preload: [:events] + + rel = + @repo.one( + from(r in Relationship, + where: r.friend_id == ^id1 and r.relation_id == ^id2, + preload: [:events] + ) ) - ) + if rel == nil do @repo.one( - from(r in Relationship, - where: r.friend_id == ^id2 and r.relation_id == ^id1, - preload: [:events] - ) + from(r in Relationship, + where: r.friend_id == ^id2 and r.relation_id == ^id1, + preload: [:events] + ) ) else rel end end - def get_or_new(a,b) do + def get_or_create(a, b) do case get(a, b) do - nil -> new(a,b) + nil -> new(a, b) relationship -> relationship end end def get_by_slugs([slug1, slug2]) do - friend1 = slug1 |> Friend.get_by_slug - friend2 = slug2 |> Friend.get_by_slug + friend1 = slug1 |> Friend.get_by_slug() + friend2 = slug2 |> Friend.get_by_slug() get(friend1, friend2) end @@ -131,22 +138,33 @@ defmodule Friends.Relationship do def age(relationship) do relationship.events - |> Enum.map(fn(event) -> - Date.diff(Date.utc_today, event.date) - end) |> Enum.sort |> List.last |> div(365) + |> Enum.map(fn event -> + Date.diff(Date.utc_today(), event.date) + end) + |> Enum.sort() + |> List.last() + |> div(365) end - def load_events(%Relationship{ - events: %Ecto.Association.NotLoaded{}} = model) do + def load_events( + %Relationship{ + events: %Ecto.Association.NotLoaded{} + } = model + ) do model |> @repo.preload([:events]) end + def load_events(%Relationship{} = r), do: r - def load_preloads(%Relationship{ - events: %Ecto.Association.NotLoaded{}} = model) do + + def load_preloads( + %Relationship{ + events: %Ecto.Association.NotLoaded{} + } = model + ) do model |> @repo.preload([:events]) end - def load_preloads(%Relationship{} = r), do: r + def load_preloads(%Relationship{} = r), do: r end diff --git a/friends/lib/friends_web/live/components/components.ex b/friends/lib/friends_web/live/components/components.ex index aba518d..4a49a90 100644 --- a/friends/lib/friends_web/live/components/components.ex +++ b/friends/lib/friends_web/live/components/components.ex @@ -2,6 +2,7 @@ defmodule FriendsWeb.FriendsLive.Components do use FriendsWeb, :live_component use Phoenix.HTML import Helpers + import FriendsWeb.LiveHelpers alias Friends.Friend alias FriendsWeb.Components.{Autocomplete, Map} alias Phoenix.LiveView.JS @@ -89,12 +90,13 @@ defmodule FriendsWeb.FriendsLive.Components do <%= for event <- @friend |> Friends.Friend.get_events do %> <% end %> <%= if @friend |> Friends.Friend.get_events |> Enum.empty? do %> -
None yet.
+
No events on record yet.
<% end %> """ @@ -102,7 +104,7 @@ defmodule FriendsWeb.FriendsLive.Components do def show_page(:relationships, assigns) do ~H""" -
+
<%= for relation <- @friend |> relations do %> <% relationship = relation(@friend, relation) %>
@@ -119,7 +121,7 @@ defmodule FriendsWeb.FriendsLive.Components do
<% end %> <%= if @friend |> relations |> Enum.empty? do %> -
No relationships on record yet.
+
No relationships on record yet.
<% end %>
""" diff --git a/friends/lib/friends_web/live/friend.ex b/friends/lib/friends_web/live/friend.ex index b529f7f..7080d10 100644 --- a/friends/lib/friends_web/live/friend.ex +++ b/friends/lib/friends_web/live/friend.ex @@ -1,7 +1,5 @@ defmodule FriendsWeb.FriendsLive.Friend do use FriendsWeb, :live_view - - alias FriendsWeb.FriendsLive.Components alias FriendsWeb.Router.Helpers, as: Routes alias Friends.Friend diff --git a/friends/lib/friends_web/live/show.ex b/friends/lib/friends_web/live/show.ex index 6f6fb28..dfa5377 100644 --- a/friends/lib/friends_web/live/show.ex +++ b/friends/lib/friends_web/live/show.ex @@ -22,7 +22,8 @@ defmodule FriendsWeb.FriendsLive.Show do |> assign(:latlon, latlon |> Poison.encode!()) |> title(friend.name <> " - " <> (live_action |> titlecase)) |> assign(:changeset, %Friend{} |> Friend.changeset()) - |> assign(:action, editable)} + |> assign(:editable, editable) + |> assign(:action, :moot)} else {:ok, socket |> redirect(to: Routes.friends_show_path(socket, :overview, friend.slug))} end diff --git a/friends/lib/friends_web/live/show.html.heex b/friends/lib/friends_web/live/show.html.heex index 8ed02aa..9f4d06c 100644 --- a/friends/lib/friends_web/live/show.html.heex +++ b/friends/lib/friends_web/live/show.html.heex @@ -6,7 +6,7 @@ <%= if @editable do %>
- <.link navigate={Routes.friends_edit_path(FriendsWeb.Endpoint, :overview, @friend.slug)} class="btn btn-block md:btn-wide text-white">edit + <.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%>
<% end %> diff --git a/friends/lib/friends_web/views/live_helpers.ex b/friends/lib/friends_web/views/live_helpers.ex index d5ffb82..731db1c 100644 --- a/friends/lib/friends_web/views/live_helpers.ex +++ b/friends/lib/friends_web/views/live_helpers.ex @@ -16,23 +16,44 @@ defmodule FriendsWeb.LiveHelpers do display_phone(phone) end end + def display_phone(phone) do """ TODO: Actually implement this """ has_plus = phone |> String.starts_with?("+") - case phone |> String.length do + + case phone |> String.length() do 10 -> 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}" - IO.inspect "#{country}(#{area}) #{first}-#{sec1}#{sec2}" + 11 when has_plus -> phone + + 12 when has_plus -> + phone 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 user = case user_token do diff --git a/friends/lib/helpers/helpers.ex b/friends/lib/helpers/helpers.ex index 39a02bc..457fb00 100644 --- a/friends/lib/helpers/helpers.ex +++ b/friends/lib/helpers/helpers.ex @@ -74,16 +74,20 @@ defmodule Helpers do birthday(friend) |> Date.diff(Date.utc_today()) end - def relations(friend) do - [friend.relationships, friend.reverse_relationships] - |> List.flatten() + def relations(friend, include_self \\ false) do + list = + [friend.relationships, friend.reverse_relationships] + |> List.flatten() + |> Enum.dedup() + + if include_self, do: list, else: list |> Enum.filter(&(&1.id != friend.id)) end def relation(friend, friend2) do Friends.Relationship.get(friend, friend2) end - def events(relationship) do + def events(%Friends.Relationship{} = relationship) do relationship.events end end