Compare commits
No commits in common. "90007d40e79c1106d4500003ece75377ad0ecf07" and "29474f49c1a40fe3fde0b42c902d2c39396688c3" have entirely different histories.
90007d40e7
...
29474f49c1
@ -24,12 +24,10 @@ config :friends, FriendsWeb.Endpoint,
|
||||
#
|
||||
# For production it's recommended to configure a different adapter
|
||||
# at the `config/runtime.exs`.
|
||||
config :friends, Friends.Mailer,
|
||||
adapter: Swoosh.Adapters.Postmark,
|
||||
api_key: "9f88862b-b2b3-46bf-9d50-31ebc2f7820c"
|
||||
config :friends, Friends.Mailer, adapter: Swoosh.Adapters.Local
|
||||
|
||||
# Swoosh API client is needed for adapters other than SMTP.
|
||||
# config :swoosh, :api_client, false
|
||||
config :swoosh, :api_client, false
|
||||
|
||||
# Configure esbuild (the version is required)
|
||||
config :esbuild,
|
||||
|
||||
@ -2,8 +2,6 @@ defmodule Friends.Accounts.User do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@repo Friends.Repo
|
||||
|
||||
schema "users" do
|
||||
field :email, :string
|
||||
field :password, :string, virtual: true, redact: true
|
||||
@ -11,17 +9,6 @@ defmodule Friends.Accounts.User do
|
||||
field :confirmed_at, :naive_datetime
|
||||
|
||||
timestamps()
|
||||
|
||||
has_one :profile, Friends.Friend
|
||||
end
|
||||
|
||||
def load_profile(%Friends.Accounts.User{profile: %Ecto.Association.NotLoaded{}} = model) do
|
||||
model
|
||||
|> @repo.preload(:profile)
|
||||
end
|
||||
|
||||
def load_profile(nil) do
|
||||
%{profile: nil}
|
||||
end
|
||||
|
||||
@doc """
|
||||
@ -60,7 +47,7 @@ defmodule Friends.Accounts.User do
|
||||
defp validate_password(changeset, opts) do
|
||||
changeset
|
||||
|> validate_required([:password])
|
||||
|> validate_length(:password, min: 6, max: 72)
|
||||
|> validate_length(:password, min: 12, max: 72)
|
||||
# |> validate_format(:password, ~r/[a-z]/, message: "at least one lower case character")
|
||||
# |> validate_format(:password, ~r/[A-Z]/, message: "at least one upper case character")
|
||||
# |> validate_format(:password, ~r/[!?@#$%^&*_0-9]/, message: "at least one digit or punctuation character")
|
||||
|
||||
@ -8,7 +8,7 @@ defmodule Friends.Accounts.UserNotifier do
|
||||
email =
|
||||
new()
|
||||
|> to(recipient)
|
||||
|> from({"Friends App", "ryan@pandu.ski"})
|
||||
|> from({"Friends", "contact@example.com"})
|
||||
|> subject(subject)
|
||||
|> text_body(body)
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ defmodule Friends.Friend do
|
||||
field(:slug, :string)
|
||||
field(:memories, {:array, :string})
|
||||
|
||||
belongs_to :user, Friends.Accounts.User, foreign_key: :user_id
|
||||
# has_many(:photos, Media.Photo)
|
||||
|
||||
many_to_many(
|
||||
:relationships,
|
||||
@ -35,26 +35,20 @@ defmodule Friends.Friend do
|
||||
|
||||
def changeset(friend, params \\ %{}) do
|
||||
friend
|
||||
|> Ecto.Changeset.cast(params, [:name, :born, :nickname, :email, :phone, :slug, :user_id])
|
||||
|> Ecto.Changeset.cast(params, [:name, :born, :nickname, :email, :phone, :slug])
|
||||
|> Ecto.Changeset.validate_required([:name, :email, :phone, :born])
|
||||
|> Ecto.Changeset.validate_format(:name, ~r/\w+\ \w+/)
|
||||
|> Ecto.Changeset.validate_format(
|
||||
:email,
|
||||
Regex.compile!("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$")
|
||||
)
|
||||
|> Ecto.Changeset.validate_format(
|
||||
:phone,
|
||||
Regex.compile!("^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$")
|
||||
)
|
||||
|> Ecto.Changeset.validate_format(:email, Regex.compile!("^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$"))
|
||||
|> Ecto.Changeset.validate_format(:phone, Regex.compile!("^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$"))
|
||||
|> Ecto.Changeset.unique_constraint(:name)
|
||||
|> Ecto.Changeset.unique_constraint(:email)
|
||||
end
|
||||
|
||||
def all() do
|
||||
preloads = [:relationships, :reverse_relationships, :user]
|
||||
preloads = [:relationships, :reverse_relationships]
|
||||
@repo.all(from(f in Friends.Friend, preload: ^preloads))
|
||||
end
|
||||
|
||||
|
||||
def new(params \\ %{}) do
|
||||
%Friend{id: "new"}
|
||||
|> struct(params)
|
||||
@ -68,7 +62,6 @@ defmodule Friends.Friend do
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_by_id(id) do
|
||||
@repo.one(
|
||||
from(f in Friend,
|
||||
@ -78,25 +71,14 @@ defmodule Friends.Friend do
|
||||
)
|
||||
end
|
||||
|
||||
def get_by_email(email) do
|
||||
@repo.one(
|
||||
from(f in Friend,
|
||||
where: f.email == ^email,
|
||||
preload: [:relationships, :reverse_relationships]
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def create_or_update(params) do
|
||||
case params.id do
|
||||
"new" ->
|
||||
%Friend{}
|
||||
|> Friend.changeset(%{params | id: nil})
|
||||
%Friend{} |> Friend.changeset(%{params | id: nil})
|
||||
|> Map.put(:action, :insert)
|
||||
|> @repo.insert!
|
||||
|
||||
_number ->
|
||||
Friend.get_by_id(params.id |> String.to_integer())
|
||||
Friend.get_by_id(params.id |> String.to_integer)
|
||||
|> Friend.changeset(params)
|
||||
|> Map.put(:action, :update)
|
||||
|> @repo.update!
|
||||
@ -106,33 +88,32 @@ defmodule Friends.Friend do
|
||||
def get_relationships(friend) do
|
||||
friend
|
||||
|> relations
|
||||
|> Enum.map(&relation(friend, &1))
|
||||
|> Enum.map(&(relation(friend, &1)))
|
||||
end
|
||||
|
||||
def get_events(friend) do
|
||||
friend
|
||||
|> get_relationships
|
||||
|> Enum.map(& &1.events)
|
||||
|> List.flatten()
|
||||
|> Enum.map(&(&1.events))
|
||||
|> List.flatten
|
||||
end
|
||||
|
||||
def age(friend) do
|
||||
# Age in years
|
||||
Date.diff(Date.utc_today(), friend.born) |> div(365)
|
||||
Date.diff(Date.utc_today,friend.born) |> div(365)
|
||||
end
|
||||
|
||||
# TODO: Refactor
|
||||
def create_birth_event(friend) do
|
||||
if is_nil(
|
||||
@repo.all(
|
||||
if is_nil @repo.all(
|
||||
from(e in Friends.Event,
|
||||
where: e.name == "born"
|
||||
)
|
||||
)
|
||||
|> List.flatten()
|
||||
) do
|
||||
)) |> List.flatten do
|
||||
else
|
||||
"find"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
defmodule FriendsWeb.FriendController do
|
||||
use FriendsWeb, :controller
|
||||
alias Friends.{Friend, Relationship}
|
||||
alias Friends.Accounts.User
|
||||
import Helpers
|
||||
|
||||
def index(conn, _params) do
|
||||
|
||||
conn
|
||||
|> assign(:all_friends, Friend.all)
|
||||
|> render("index.html")
|
||||
end
|
||||
end
|
||||
@ -1,63 +1,16 @@
|
||||
defmodule FriendsWeb.PageController do
|
||||
use FriendsWeb, :controller
|
||||
alias Friends.{Friend, Repo, Accounts.User}
|
||||
|
||||
import Helpers.Names
|
||||
|
||||
plug :assign_profile
|
||||
alias Friends.{Friend, Relationship}
|
||||
import Helpers
|
||||
|
||||
def index(conn, _params) do
|
||||
new_friend = Friend.new() |> Friend.changeset()
|
||||
|
||||
new_friend = Friend.new |> Friend.changeset
|
||||
|
||||
conn
|
||||
|> assign(:new_friend, new_friend)
|
||||
|> assign(:all_friends, Friend.all())
|
||||
|> assign(:users, User |> Repo.all())
|
||||
|> assign(:all_friends, Friend.all)
|
||||
|> assign(:users, Friends.Accounts.User |> Friends.Repo.all)
|
||||
|> render("index.html")
|
||||
end
|
||||
|
||||
defp assign_profile(conn, _opts) do
|
||||
current_user = conn.assigns.current_user
|
||||
|
||||
case current_user do
|
||||
nil ->
|
||||
# No logged in user, and no profile
|
||||
conn
|
||||
|
||||
user ->
|
||||
case user.profile do
|
||||
nil ->
|
||||
# Logged in user, but no profile
|
||||
# Is there a profile with the same email?
|
||||
try_profile = user.email |> Friends.Friend.get_by_email()
|
||||
|
||||
case try_profile do
|
||||
nil ->
|
||||
# If not, we need to make a new profile
|
||||
conn
|
||||
|> redirect(to: Routes.profile_form_path(conn, :new))
|
||||
|
||||
# If so, link 'em and be done with it
|
||||
friend ->
|
||||
friend
|
||||
|> Friends.Friend.changeset(%{
|
||||
user_id: user.id
|
||||
})
|
||||
|> Friends.Repo.update!()
|
||||
|
||||
conn
|
||||
|> put_flash(
|
||||
:info,
|
||||
"Welcome to the app, #{friend |> first_name()}!"
|
||||
)
|
||||
|> redirect(to: Routes.friend_path(conn, :index))
|
||||
end
|
||||
|
||||
_profile ->
|
||||
# Logged in user, and linked profile
|
||||
conn
|
||||
|> redirect(to: Routes.friend_path(conn, :index))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -91,13 +91,7 @@ defmodule FriendsWeb.UserAuth do
|
||||
def fetch_current_user(conn, _opts) do
|
||||
{user_token, conn} = ensure_user_token(conn)
|
||||
user = user_token && Accounts.get_user_by_session_token(user_token)
|
||||
|
||||
assign(
|
||||
conn,
|
||||
:current_user,
|
||||
user
|
||||
|> Friends.Repo.preload(:profile)
|
||||
)
|
||||
assign(conn, :current_user, user)
|
||||
end
|
||||
|
||||
defp ensure_user_token(conn) do
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
defmodule FriendsWeb.UserProfileController do
|
||||
use FriendsWeb, :controller
|
||||
|
||||
alias Friends.Accounts
|
||||
alias FriendsWeb.Router.Helpers, as: Routes
|
||||
|
||||
plug :assign_profile
|
||||
|
||||
|
||||
|
||||
@doc """
|
||||
Checks if the user has linked a profile yet,
|
||||
and redirects to the profile creation flow
|
||||
if not.
|
||||
"""
|
||||
def main(conn) do
|
||||
conn
|
||||
|> put_flash(:info, "test")
|
||||
end
|
||||
|
||||
defp assign_profile(conn, _opts) do
|
||||
user = conn.assigns.current_user
|
||||
|
||||
msg = case user.profile do
|
||||
nil -> "No profile!"
|
||||
profile -> "Yes profile! #{profile.name}"
|
||||
end
|
||||
|
||||
conn
|
||||
|> put_flash(:info, msg)
|
||||
end
|
||||
end
|
||||
@ -21,10 +21,8 @@ defmodule FriendsWeb.Router do
|
||||
pipe_through :browser
|
||||
|
||||
get "/", PageController, :index
|
||||
get "/friends", FriendController, :index
|
||||
live "/friend/:slug", FriendsLive.Show
|
||||
|
||||
live "/friend/:slug", FriendLive.Show
|
||||
live "/friend/:slug/edit", FriendLive.Edit
|
||||
end
|
||||
|
||||
# Other scopes may use custom stacks.
|
||||
@ -65,7 +63,6 @@ defmodule FriendsWeb.Router do
|
||||
|
||||
scope "/", FriendsWeb do
|
||||
pipe_through [:browser, :redirect_if_user_is_authenticated]
|
||||
# Requires the user NOT be authenticated:
|
||||
|
||||
get "/users/register", UserRegistrationController, :new
|
||||
post "/users/register", UserRegistrationController, :create
|
||||
@ -79,9 +76,6 @@ defmodule FriendsWeb.Router do
|
||||
|
||||
scope "/", FriendsWeb do
|
||||
pipe_through [:browser, :require_authenticated_user]
|
||||
# Requires the user DO be authenticated:
|
||||
|
||||
live "/profile/new", ProfileLive.Form, :new
|
||||
|
||||
get "/users/settings", UserSettingsController, :edit
|
||||
put "/users/settings", UserSettingsController, :update
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
<h1>All Friends</h1>
|
||||
|
||||
<ul class="text-xl">
|
||||
<%= for f <- @all_friends do %>
|
||||
<li>
|
||||
<.link href={"/friend/#{f.slug}"}><%= f.name %></.link>
|
||||
<%= if f.id == @current_user.profile.id do %>
|
||||
(you)
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
|
||||
@ -1,16 +1,10 @@
|
||||
<ul class="mt-3 p-2 shadow menu menu-compact dropdown-content bg-base-100 text-neutral rounded-box w-52">
|
||||
<%= if @current_user do %>
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img src="https://placeimg.com/80/80/people" />
|
||||
</div>
|
||||
</label>
|
||||
<ul class="mt-3 p-2 shadow menu menu-compact dropdown-content bg-base-100 text-neutral rounded-box w-52">
|
||||
<li><%= @current_user.email %></li>
|
||||
<li><%= link "Settings", to: Routes.user_settings_path(@conn, :edit) %></li>
|
||||
<li><%= link "Log out", to: Routes.user_session_path(@conn, :delete), method: :delete %></li>
|
||||
</ul>
|
||||
<% else %>
|
||||
<%= link "Log in", to: Routes.user_session_path(@conn, :new), class: "btn" %>
|
||||
<%= link "Register", to: Routes.user_registration_path(@conn, :new), class: "btn btn-primary" %>
|
||||
<li><%= link "Register", to: Routes.user_registration_path(@conn, :new) %></li>
|
||||
<li><%= link "Log in", to: Routes.user_session_path(@conn, :new) %></li>
|
||||
<% end %>
|
||||
|
||||
</ul>
|
||||
|
||||
@ -4,14 +4,17 @@
|
||||
<.link navigate={"/"} class="btn btn-ghost normal-case text-xl">Friends</.link>
|
||||
</div>
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img src="https://placeimg.com/80/80/people" />
|
||||
</div>
|
||||
</label>
|
||||
<%= render("_user_menu.html", conn: @conn, current_user: @current_user) %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mx-auto w-full lg:w-3/4">
|
||||
<main class="container p-4 mb-10 md:p-8 prose">
|
||||
<p class="alert alert-info" role="alert"><%= get_flash(@conn, :info) %></p>
|
||||
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
|
||||
<%= @inner_content %>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -3,8 +3,27 @@
|
||||
<div class="flex-1">
|
||||
<.link navigate={"/"} class="btn btn-ghost normal-case text-xl">Friends</.link>
|
||||
</div>
|
||||
<div class="flex-none gap-2">
|
||||
<div class="form-control hidden md:block">
|
||||
<input type="text" placeholder="Search" class="input input-bordered" />
|
||||
</div>
|
||||
<div class="dropdown dropdown-end">
|
||||
<%= render("_user_menu.html", conn: @socket, current_user: @current_user) %>
|
||||
<label tabindex="0" class="btn btn-ghost btn-circle avatar">
|
||||
<div class="w-10 rounded-full">
|
||||
<img src="https://placeimg.com/80/80/people" />
|
||||
</div>
|
||||
</label>
|
||||
<ul tabindex="0" class="mt-3 p-2 shadow menu menu-compact dropdown-content bg-base-100 rounded-box w-52">
|
||||
<li>
|
||||
<a>
|
||||
Profile
|
||||
</a>
|
||||
</li>
|
||||
<li class="md:hidden"><a>Search</a></li>
|
||||
<li><a>Settings</a></li>
|
||||
<li><a>Logout</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
defmodule FriendsWeb.ProfileLive.Form do
|
||||
use FriendsWeb, :live_view
|
||||
import FriendsWeb.LiveView
|
||||
|
||||
def mount(%{}, %{"user_token" => token}, socket) do
|
||||
{:ok,
|
||||
socket
|
||||
|> assign_current_user(token)
|
||||
|> title("Create a profile")}
|
||||
end
|
||||
|
||||
def show_form(%{step: 1} = assigns) do
|
||||
# First things first, are we linking to an existing user or no?
|
||||
|
||||
~H"""
|
||||
<b>Your email:</b> <%= @current_user.email %>
|
||||
"""
|
||||
end
|
||||
|
||||
def show_form(%{step: 2} = assigns) do
|
||||
~H"""
|
||||
Step 2
|
||||
"""
|
||||
end
|
||||
end
|
||||
@ -1,9 +0,0 @@
|
||||
<div class="prose">
|
||||
<h1>Welcome!</h1>
|
||||
<p>You have made a user account, but we don't yet have a profile for you.</p>
|
||||
|
||||
<div id="profile-form">
|
||||
<.show_form step={1} current_user={@current_user} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@ -1,6 +0,0 @@
|
||||
defmodule FriendsWeb.FriendView do
|
||||
use FriendsWeb, :view
|
||||
import Phoenix.Component
|
||||
import Helpers
|
||||
|
||||
end
|
||||
@ -1,18 +0,0 @@
|
||||
defmodule FriendsWeb.LiveView do
|
||||
use FriendsWeb, :live_component
|
||||
|
||||
def assign_current_user(socket, user_token) do
|
||||
socket
|
||||
|> assign(
|
||||
:current_user,
|
||||
user_token
|
||||
|> Friends.Accounts.get_user_by_session_token()
|
||||
|> Friends.Repo.preload(:profile)
|
||||
)
|
||||
end
|
||||
|
||||
def title(socket, title) do
|
||||
socket
|
||||
|> assign(:page_title, title)
|
||||
end
|
||||
end
|
||||
@ -1,3 +0,0 @@
|
||||
defmodule UserProfileView do
|
||||
|
||||
end
|
||||
@ -1,4 +1,6 @@
|
||||
defmodule Helpers do
|
||||
import Helpers.Names
|
||||
|
||||
def do!(thing) do
|
||||
case thing do
|
||||
{:ok, answer} -> answer
|
||||
@ -6,21 +8,21 @@ defmodule Helpers do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def pluralize(qty, word) do
|
||||
plural =
|
||||
if qty == 1 do
|
||||
plural = if qty == 1 do
|
||||
"#{word}"
|
||||
else
|
||||
"#{word}s"
|
||||
end
|
||||
|
||||
"#{qty} #{plural}"
|
||||
end
|
||||
|
||||
def parse_date(str), do: str |> to_string |> Timex.parse("%Y-%m-%d", :strftime)
|
||||
|
||||
def format_phone(str) do
|
||||
str |> String.replace(" ", "") |> String.replace("-", "") |> String.replace(".", "")
|
||||
str
|
||||
|> String.replace(" ", "") |> String.replace("-", "") |> String.replace(".", "")
|
||||
end
|
||||
|
||||
def to_slug(name) when is_binary(name) do
|
||||
@ -28,7 +30,6 @@ defmodule Helpers do
|
||||
|> String.replace(" ", "-")
|
||||
|> String.downcase()
|
||||
end
|
||||
|
||||
def to_slug(obj), do: to_slug(obj.name)
|
||||
|
||||
def from_slug(name) do
|
||||
@ -40,20 +41,17 @@ defmodule Helpers do
|
||||
end
|
||||
|
||||
def birthday(friend) do
|
||||
this_year =
|
||||
Date.utc_today()
|
||||
this_year = (Date.utc_today
|
||||
|> Calendar.strftime("%Y")
|
||||
|> String.to_integer()
|
||||
|> String.to_integer)
|
||||
|
||||
next_birthday =
|
||||
friend.born
|
||||
next_birthday = friend.born
|
||||
|> Calendar.strftime("%m-%d")
|
||||
|
||||
next_birthdate =
|
||||
"#{this_year}-#{next_birthday}"
|
||||
next_birthdate = "#{this_year}-#{next_birthday}"
|
||||
|> Date.from_iso8601!()
|
||||
|
||||
if next_birthdate |> Date.diff(Date.utc_today()) < 0 do
|
||||
if next_birthdate |> Date.diff(Date.utc_today) < 0 do
|
||||
"#{this_year + 1}-#{next_birthday}" |> Date.from_iso8601!()
|
||||
else
|
||||
next_birthdate
|
||||
@ -61,7 +59,7 @@ defmodule Helpers do
|
||||
end
|
||||
|
||||
def time_until_birthday(friend) do
|
||||
birthday(friend) |> Date.diff(Date.utc_today())
|
||||
birthday(friend) |> Date.diff(Date.utc_today)
|
||||
end
|
||||
|
||||
def relations(friend) do
|
||||
@ -76,4 +74,5 @@ defmodule Helpers do
|
||||
def events(relationship) do
|
||||
relationship.events
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
defmodule Friends.Repo.Migrations.ProfileBelongsToUser do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:friends) do
|
||||
add :user_id, references(:users)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -3,6 +3,6 @@ defmodule FriendsWeb.PageControllerTest do
|
||||
|
||||
test "GET /", %{conn: conn} do
|
||||
conn = get(conn, "/")
|
||||
assert html_response(conn, 200) =~ "Friends App"
|
||||
assert html_response(conn, 200) =~ "Welcome to Phoenix!"
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
Reference in New Issue
Block a user