An example AT Protocol application, written in Elixir using atex and Drinkup.
9
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: login page

+88 -33
+6
.vscode/settings.json
··· 1 + { 2 + "tailwindCSS.includeLanguages": { 3 + "phoenix-heex": "html", 4 + "elixir": "html" 5 + } 6 + }
+6
assets/css/app.css
··· 106 106 } 107 107 108 108 /* This file is for your main application CSS */ 109 + 110 + @layer components { 111 + .button { 112 + @apply bg-blue-500 text-white px-3 py-1 rounded-full; 113 + } 114 + }
+5 -29
lib/statusphere_web/components/layouts.ex
··· 35 35 36 36 def app(assigns) do 37 37 ~H""" 38 - <header class="navbar px-4 sm:px-6 lg:px-8"> 39 - <div class="flex-1"> 40 - <a href="/" class="flex-1 flex w-fit items-center gap-2"> 41 - <img src={~p"/images/logo.svg"} width="36" /> 42 - <span class="text-sm font-semibold">v{Application.spec(:phoenix, :vsn)}</span> 43 - </a> 44 - </div> 45 - <div class="flex-none"> 46 - <ul class="flex flex-column px-1 space-x-4 items-center"> 47 - <li> 48 - <a href="https://phoenixframework.org/" class="btn btn-ghost">Website</a> 49 - </li> 50 - <li> 51 - <a href="https://github.com/phoenixframework/phoenix" class="btn btn-ghost">GitHub</a> 52 - </li> 53 - <li> 54 - <.theme_toggle /> 55 - </li> 56 - <li> 57 - <a href="https://hexdocs.pm/phoenix/overview.html" class="btn btn-primary"> 58 - Get Started <span aria-hidden="true">&rarr;</span> 59 - </a> 60 - </li> 61 - </ul> 62 - </div> 38 + <header class="text-center py-4 flex flex-col gap-4"> 39 + <h1 class="font-bold text-8xl">Statusphere</h1> 40 + <p>Set your status on the Atmosphere</p> 63 41 </header> 64 42 65 - <main class="px-4 py-20 sm:px-6 lg:px-8"> 66 - <div class="mx-auto max-w-2xl space-y-4"> 67 - {render_slot(@inner_block)} 68 - </div> 43 + <main class="flex flex-col gap-2 p-4 max-w-[600px] mx-auto"> 44 + {render_slot(@inner_block)} 69 45 </main> 70 46 71 47 <.flash_group flash={@flash} />
+22 -1
lib/statusphere_web/controllers/page_controller.ex
··· 2 2 use StatusphereWeb, :controller 3 3 4 4 def home(conn, _params) do 5 - render(conn, :home) 5 + # TODO: fetch bluesky profile instead 6 + session = get_session(conn, :atex_oauth) 7 + 8 + identity = 9 + with %{did: did} <- session, 10 + {:ok, ident} <- Atex.IdentityResolver.resolve(did) do 11 + ident 12 + else 13 + _ -> nil 14 + end 15 + 16 + render(conn, :home, identity: identity) 17 + end 18 + 19 + def login(conn, _params) do 20 + render(conn, :login) 21 + end 22 + 23 + def logout(conn, _params) do 24 + conn 25 + |> delete_session(:atex_oauth) 26 + |> redirect(to: "/") 6 27 end 7 28 end
+23 -2
lib/statusphere_web/controllers/page_html/home.html.heex
··· 1 1 <Layouts.app flash={@flash}> 2 - <div>hello</div> 3 - </Layouts.app> 2 + <div class="flex items-center justify-between"> 3 + <%= if @identity == nil do %> 4 + <p> 5 + <.link navigate="/login" class="text-blue-500 underline">Log in</.link> 6 + to set your status! 7 + </p> 8 + <.link 9 + navigate="/login" 10 + class="button" 11 + > 12 + Log in 13 + </.link> 14 + <% else %> 15 + <p>Hi, <b>{@identity.handle}</b>. What's your status today?</p> 16 + <.link 17 + navigate="/logout" 18 + class="button" 19 + > 20 + Log out 21 + </.link> 22 + <% end %> 23 + </div> 24 + </Layouts.app>
+19
lib/statusphere_web/controllers/page_html/login.html.heex
··· 1 + <Layouts.app flash={@flash}> 2 + <form 3 + id="login-form" 4 + action="/oauth/login" 5 + method="get" 6 + class="flex gap-2 border rounded px-4 py-2 outline-blue-500 focus-within:outline" 7 + > 8 + <input 9 + type="text" 10 + name="handle" 11 + placeholder="Enter your handle (eg. alice.bsky.social)" 12 + required 13 + class="grow outline-none" 14 + /> 15 + <button type="submit" class="button"> 16 + Log in 17 + </button> 18 + </form> 19 + </Layouts.app>
+2
lib/statusphere_web/router.ex
··· 18 18 pipe_through :browser 19 19 20 20 get "/", PageController, :home 21 + get "/logout", PageController, :logout 22 + get "/login", PageController, :login 21 23 end 22 24 23 25 forward "/oauth", Atex.OAuth.Plug, callback: {__MODULE__, :oauth_callback, []}
+5 -1
test/statusphere_elixir_web/controllers/page_controller_test.exs
··· 3 3 4 4 test "GET /", %{conn: conn} do 5 5 conn = get(conn, ~p"/") 6 - assert html_response(conn, 200) =~ "Peace of mind from prototype to production" 6 + response = html_response(conn, 200) 7 + assert response =~ "Statusphere" 8 + assert response =~ "Set your status on the Atmosphere" 9 + assert response =~ "Log in" 10 + assert response =~ "to set your status!" 7 11 end 8 12 end