9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ElixirAdvent Calendar 2024

Day 15

ElixirDesktopで作るスマホアプリ Part 11 ログアウト、ユーザー削除、利用規約などのアプリ全体の設定

Posted at

はじめに

この記事はElixirアドベントカレンダー2024のシリーズ2、14日目の記事です

アプリ全体の設定を作成していきます

作る機能

  1. UIに使うテキストの辞書データ作成
  2. 設定一覧画面を作成
  3. ユーザー設定を配下に追加
  4. お問い合わせを配下に追加
  5. プライバシーポリシーを配下に追加
  6. 利用規約を配下に追加
  7. ユーザー削除ボタンを追加
  8. ログアウトボタンを追加

辞書データを作成

今回使う文言を末尾に追加

priv/gettext/default.pot:L186
# Setting

msgid "General"
msgstr ""

msgid "Privacy Policy"
msgstr ""

msgid "Terms of Service"
msgstr ""

msgid "Delete Account"
msgstr ""

msgid "Sign out"
msgstr ""

msgid "Setting"
msgstr ""

msgid "Contact"
msgstr ""

以下のコマンドを実行して他の辞書に反映します

mix gettext.merge priv/gettext

日本語テキストを追加

priv/gettext/ja/LC_MESSAGES/default.po
# Setting

msgid "General"
msgstr "ユーザー設定"

msgid "Privacy Policy"
msgstr "プライバシーポリシー"

msgid "Terms of Service"
msgstr "利用規約"

msgid "Delete Account"
msgstr "アカウント削除"

msgid "Sign out"
msgstr "ログアウト"

msgid "Setting"
msgstr "設定"

msgid "Contact"
msgstr "問い合わせ"

設定一覧画面を作成

ベースとなるページを作成

lib/trarecord_web/live/setting_live/index.ex
defmodule TrarecordWeb.SettingsLive.Index do
  use TrarecordWeb, :live_view

  @impl true
  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Setting")} />
    <div id="settings" class="h-screen mt-20 flex flex-col gap-y-8 mx-2">
      <table class="table bg-white text-base">
        <tbody>
          <tr>
            <td>
              <.link class="flex justify-between" navigate={~p"/users/settings"}>
                <p><%= gettext("General") %></p>
                <.icon name="hero-chevron-right-solid" />
              </.link>
            </td>
          </tr>
        </tbody>
      </table>
    </div>
    <.bottom_tab current="Setting" />
    """
  end

  @impl true
  def mount(_params, session, socket) do
    Gettext.put_locale(TrarecordWeb.Gettext, Map.get(session, "locale", "ja"))

    {:ok, socket}
  end
end

ルートに追加

lib/trarecord_web/router.ex
  scope "/", TrarecordWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{TrarecordWeb.UserAuth, :ensure_authenticated}] do
+     live "/settings", SettingsLive, :infex
      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
      live "/onboarding", OnboardingLive.Index, :index

      live "/folders", FolderLive.Index, :index
      live "/folders/new", FolderLive.Index, :new
      live "/folders/:id/edit", FolderLive.Index, :edit
      live "/folders/:id/delete", FolderLive.Index, :delete

      live "/folders/:id", FolderLive.Show, :show
      live "/folders/:id/show/edit", FolderLive.Show, :edit
    end
  end

ナビゲーションリンクの差し替え

lib/trarecord_web/components/navigation.ex:L50
  defp links() do
    [
      {"Folder", "hero-book-open-solid", "/folders"},
-     {"Setting", "hero-cog-6-tooth-solid", "/users/settings"}
+     {"Setting", "hero-cog-6-tooth-solid", "/settings"}

    ]
  end

スクリーンショット 2024-12-30 14.26.29.png

ユーザー設定を配下に追加

導線・ページ自体は作ってあるので、戻るボタンを表示します

lib/trarecord_web/live/user_settings_live.ex
defmodule TrarecordWeb.UserSettingsLive do
  use TrarecordWeb, :live_view

  alias Trarecord.Accounts

  def render(assigns) do
    ~H"""
-   <.header_nav title={gettext("Setting")} />
+   <.header_nav title={gettext("Setting")}>
+     <:back>
+       <.link navigate={~p"/settings"}>
+         <%= gettext("Back") %>
+       </.link>
+     </:back>
+   </.header_nav>
    ...
    """
  end

-  def mount(%{"token" => token}, _session, socket) do
+  def mount(%{"token" => token}, session, socket) do
+   Gettext.put_locale(TrarecordWeb.Gettext, Map.get(session, "locale", "ja"))
    socket =
      case Accounts.update_user_email(socket.assigns.current_user, token) do
        :ok ->
          put_flash(socket, :info, gettext("Email changed successfully."))

        :error ->
          put_flash(socket, :error, "Email change link is invalid or it has expired.")
      end

    {:ok, push_navigate(socket, to: ~p"/users/settings")}
  end

- def mount(_params, _session, socket) do
+ def mount(_params, session, socket) do
+   Gettext.put_locale(TrarecordWeb.Gettext, Map.get(session, "locale", "ja"))
    user = socket.assigns.current_user
    email_changeset = Accounts.change_user_email(user)
    password_changeset = Accounts.change_user_password(user)

    socket =
      socket
      |> assign(:current_password, nil)
      |> assign(:email_form_current_password, nil)
      |> assign(:current_email, user.email)
      |> assign(:email_form, to_form(email_changeset))
      |> assign(:password_form, to_form(password_changeset))
      |> assign(:trigger_submit, false)

    {:ok, socket}
  end
  ...
end

スクリーンショット 2024-12-30 14.37.49.png

  1. お問い合わせを配下に追加

お問い合わせはgoogle formが楽なのでそちらのURLを設置します

open_safariのnative bridgeを使用して開くのでphx-hookをセットしてクリックイベントを追加します

lib/trarecord_web/live/setting_live/index.ex
defmodule TrarecordWeb.SettingLive.Index do
  use TrarecordWeb, :live_view

  @contact_form [goole form url]

  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Setting")} />
-   <div id="settings" class="h-screen my-20 flex flex-col gap-y-8 mx-2">
+   <div id="settings" class="h-screen my-20 flex flex-col gap-y-8 mx-2" phx-hook="NativeBridge">

      <table class="table bg-white text-base">
        <tbody>
          <tr>
            <td>
              <.link class="flex justify-between" navigate={~p"/users/settings"}>
                <p><%= gettext("General") %></p>
                <.icon name="hero-chevron-right-solid" />
              </.link>
            </td>
          </tr>
        </tbody>
      </table>
+     <table class="table bg-white text-base">
+       <tbody>
+         <tr>
+           <td>
+             <a
+               href={@contact_form}
+               target="_blank"
+               class="flex justify-between"
+               phx-click="open_contact"
+             >
+               <p><%= gettext("Contact") %></p>
+               <.icon name="hero-chevron-right-solid" />
+             </a>
+           </td>
+         </tr>
+       </tbody>
+     </table>
    </div>
    <.bottom_tab current="Setting" />
    """
  end

  @impl true
  def mount(_params, session, socket) do
    Gettext.put_locale(TrarecordWeb.Gettext, Map.get(session, "locale", "ja"))

-   {:ok, socket}
+   {:ok, assign(socket, :contact_form, @contact_form)}
  end

+ @impl true 
+ def handle_event("open_contact", _params, socket) do
+   {:noreply, push_event(socket, "open_safari", %{url: @contact_form})}
+ end
end  

スクリーンショット 2024-12-30 15.16.40.png

利用規約、プライバシーポリシーを配下に追加

lib/trarecord_web/live/setting_live/index.ex:L7
  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Setting")} />
    <div id="settings" class="h-screen my-20 flex flex-col gap-y-8 mx-2" phx-hook="NativeBridge">
      <table class="table bg-white text-base">
        ...
      </table>
      <table class="table bg-white text-base">
        ...
      </table>
+     <table class="table bg-white text-base">
+       <tbody>
+         <tr>
+           <td>
+             <.link class="flex justify-between" navigate={~p"/settings/gp"}>
+               <p><%= gettext("Terms of Service") %></p>
+               <.icon name="hero-chevron-right-solid" />
+             </.link>
+           </td>
+         </tr>
+         <tr>
+           <td>
+             <.link class="flex justify-between" navigate={~p"/settings/privacy"}>
+               <p><%= gettext("Privacy Policy") %></p>
+               <.icon name="hero-chevron-right-solid" />
+             </.link>
+           </td>
+         </tr>
+       </tbody>
+     </table>
    </div>
    <.bottom_tab current="Setting" />
    """
  end

利用規約のページを作ります

lib/trarecord_web/live/setting_live/gp.ex
defmodule TrarecordWeb.SettingsLive.Gp do
  use TrarecordWeb, :live_view

  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Terms of Service")}>
      <:back>
        <.link navigate={~p"/settings"}>
          <.icon name="hero-chevron-left-solid" class="h-6 w-6" />
        </.link>
      </:back>
    </.header_nav>
    <div class="flex flex-col gap-y-2 p-4 bg-white mb-20">
      <h2 class="text-xl">利用規約</h2>
      <p>
        この利用規約(以下,「本規約」といいます。)は,Trarecord(以下,「当社」といいます。)がこのウェブ上で提供するサービス(以下,「本サービス」といいます。)の利用条件を定めるものです。登録ユーザーの皆さま(以下,「ユーザー」といいます。)には,本規約に従って,本サービスをご利用いただきます。
      </p>
      
      ここに規約を書く

      <p class="tR">以上</p>
    </div>
    <.bottom_tab current="Setting" />
    """
  end

  def mount(_params, session, socket) do
    Gettext.put_locale(TrarecoWeb.Gettext, Map.get(session, "locale", "en"))
    {:ok, socket}
  end
end

プライバシーポリシーのページを作ります

lib/trarecord_web/live/setting_live/privacy.ex
defmodule TrarecoWeb.SettingsLive.Privacy do
  use TrarecordWeb, :live_view

  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Privacy Policy")}>
      <:back>
        <.link navigate={~p"/settings"}>
          <.icon name="hero-chevron-left-solid" class="h-6 w-6" />
        </.link>
      </:back>
    </.header_nav>
    <div class="flex flex-col gap-y-2 p-4 bg-white mb-20">
      <h2 class="text-xl">プライバシーポリシー</h2>
      <p>
        Trareco(以下,「当社」といいます。)は,本ウェブ上で提供するサービス(以下,「本サービス」といいます。)における,ユーザーの個人情報の取扱いについて,以下のとおりプライバシーポリシー(以下,「本ポリシー」といいます。)を定めます。
      </p>
      ここにプライバシーポリシーを書く
      <p class="tR">以上</p>
    </div>
    <.bottom_tab current="Setting" />
    """
  end

  def mount(_params, session, socket) do
    Gettext.put_locale(TrarecoWeb.Gettext, Map.get(session, "locale", "en"))
    {:ok, socket}
  end
end

ルーターに追加して完了です

lib/trarecord_web/router.ex
  scope "/", TrarecordWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{TrarecordWeb.UserAuth, :ensure_authenticated}] do
      live "/settings", SettingLive.Index, :infex
+     live "/settings/gp", SettingsLive.Gp
+     live "/settings/privacy", SettingsLive.Privacy

      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
      live "/onboarding", OnboardingLive.Index, :index

      live "/folders", FolderLive.Index, :index
      live "/folders/new", FolderLive.Index, :new
      live "/folders/:id/edit", FolderLive.Index, :edit
      live "/folders/:id/delete", FolderLive.Index, :delete

      live "/folders/:id", FolderLive.Show, :show
      live "/folders/:id/show/edit", FolderLive.Show, :edit
    end
  end

ユーザー削除ボタンを追加

削除関数を追加

lib/trarecord/accounts.ex:L61
  def get_user!(id), do: Repo.get!(User, id)

+ def delete_user(user), do: Repo.delete(user)   

削除確認モーダルと削除イベントを作ります

lib/trarecord_web/live/setting_live/index.ex
+ alias Trarecord.Accounts

  @impl true
  def render(assigns) do
    ~H"""
    <.header_nav title={gettext("Setting")} />
    <div id="settings" class="h-screen my-20 flex flex-col gap-y-8 mx-2" phx-hook="NativeBridge">
      <table class="table bg-white text-base">
      ...
      </table>
+     <.link patch={~p"/settings/delete"} class="btn btn-error text-white normal-case text-xl">
+       <%= gettext("Delete Account") %>
+     </.link>
    </div>
    <.bottom_tab current="Setting" />
+   <.modal
+     :if={@live_action in [:delete]}
+     id="user-delete-modal"
+     show
+     on_cancel={JS.navigate(~p"/settings")}
+   >
+     <p class="m-8"><%= gettext("Are you sure?") %></p>
+     <div class="flex justify-between mx-4 gap-x-4">
+       <button class="btn w-28" phx-click={JS.navigate(~p"/settings")}>
+         <%= gettext("Cancel") %>
+       </button>
+       <button
+         class="btn btn-error text-white w-28"
+         phx-click={JS.push("delete", value: %{id: @current_user.id})}
+       >
+         <%= gettext("Delete") %>
+       </button>
+     </div>
+   </.modal>
    """
  end

  @impl true
  def mount(_params, session, socket) do
    Gettext.put_locale(TrarecordWeb.Gettext, Map.get(session, "locale", "ja"))

    {:ok, assign(socket, :contact_form, @contact_form)}
  end

+ @impl true
+ def handle_params(params, _url, socket) do
+   {:noreply, apply_action(socket, socket.assigns.live_action, params)}
+ end

+ defp apply_action(socket, :delete, _params) do
+   socket
+   |> assign(:page_title, "Delete Account")
+ end

+ defp apply_action(socket, _action, _params), do: socket

  @impl true
  def handle_event("open_contact", _params, socket) do
    {:noreply, push_event(socket, "open_safari", %{url: @contact_form})}
  end
  
+ def handle_event("delete", %{"id" => id}, socket) do
+   user = Accounts.get_user!(id)
+    case Accounts.delete_user(user) do
+     {:ok, _} ->
+       File.rm(Trareco.config_dir() <> "/token")
+        {
+         :noreply,
+         socket
+         |> put_flash(:info, "Account deleted successfully")
+         |> push_navigate(to: ~p"/")
+       }
+
+     {:error, _} ->
+       {
+         :noreply,
+         socket
+         |> put_flash(:error, "Account deleted error")
+       }
+   end
  end
end 

routerに削除モーダル表示用のパスを追加

lib/trarecord_web/router.ex
  scope "/", TrarecordWeb do
    pipe_through [:browser, :require_authenticated_user]

    live_session :require_authenticated_user,
      on_mount: [{TrarecordWeb.UserAuth, :ensure_authenticated}] do
      live "/settings", SettingLive.Index, :infex
+     live "/settings/delete", SettingsLive.Index, :delete      
      live "/settings/gp", SettingsLive.Gp
      live "/settings/privacy", SettingsLive.Privacy

      live "/users/settings", UserSettingsLive, :edit
      live "/users/settings/confirm_email/:token", UserSettingsLive, :confirm_email
      live "/onboarding", OnboardingLive.Index, :index

      live "/folders", FolderLive.Index, :index
      live "/folders/new", FolderLive.Index, :new
      live "/folders/:id/edit", FolderLive.Index, :edit
      live "/folders/:id/delete", FolderLive.Index, :delete

      live "/folders/:id", FolderLive.Show, :show
      live "/folders/:id/show/edit", FolderLive.Show, :edit
    end
  end

ログアウトボタンを追加

リンクとログアウト機能自体はあるのでリンクを設置して完了です

lib/trarecord_web/live/setting_live/index.ex:L61
      <.link patch={~p"/settings/delete"} class="btn btn-error text-white normal-case text-xl">
        <%= gettext("Delete Account") %>
      </.link>
+     <.link
+       method="delete"
+       href="/users/log_out"
+       class="btn btn-error text-white normal-case text-xl"
+     >
+       <%= gettext("Sign out") %>
+     </.link>

スクリーンショット 2024-12-31 22.48.03.png

最後に

全体の設定画面を作成して、問い合わせフォーム、利用規約、プライバシーポリシー等のアプリの運用上必要なものの設置

ログアウト、ユーザー削除を実装しました

次はGoogle Places APIからスポット情報を取得する方法を解説します

本記事は以上になりますありがとうございました

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?