iT邦幫忙

2021 iThome 鐵人賽

DAY 20
0

昨天用 tailwind 畫好首頁
現在要做開始遊戲前的等待對手頁面

把遊戲放進去 phoenix 裡面

在這之前,我覺得差不多可以把 game 搬到 phoenix 裡面了
就..把他移動到 lib/card/game.ex
但我們還是要調整一下 module 名稱
Game 變成 Card.Game,同時裡面有一些用 %Game{} 的地方,
也要改,不過既然是在裡面用的 我們可以寫 %__MODULE__{} 就好

房間狀態

接著我們需要另一個 struct 來儲存房間狀態

這個也是 lib/card/room.ex

defmodule Card.Room do
  defstruct id: nil, game: nil, game_pid: nil, host_ready: false, guest_ready: false
end

這個 struct 就可以替換掉我們昨天暫時寫的 "房間狀態"
在昨天的 lib/card_web/live/page_live/index.ex 最後兩個方法

  def handle_event("start_game", _params, socket) do
    room_id = random_room_id()
    :ets.insert_new(:rooms, {room_id, %Card.Room{id: room_id}})

    {:noreply, push_redirect(socket, to: "/#{room_id}")}
  end

  # 這個跟昨天比少了 "room_" 我發現那個好像有點沒意義
  defp random_room_id(), do: String.trim(to_string(:rand.uniform), "0.")

打點好之後我們就可以來 router 幫路徑補上

scope "/", CardWeb do
  pipe_through :browser

  live "/", PageLive.Index, :index
  live "/:id/host", GameLive.Invite, :host
  live "/:id/guest", GameLive.Invite, :guest
end

這邊會分成兩個,host 結尾的是我們要從 page index 導出去的連結
guest 是給我們複製貼給對手用的

寫好之後在終端機打 mix phx.routes
他就會回傳我們現在設定好的連結,跟 router helper 名稱

    page_index_path  GET  /                                      Phoenix.LiveView.Plug :index
   game_invite_path  GET  /:id/host                              Phoenix.LiveView.Plug :host
   game_invite_path  GET  /:id/guest                             Phoenix.LiveView.Plug :guest
   下略...

我們在 page index 按下開始遊戲按鈕後,就可以使用 Routes.game_invite_path(socket, :host, room_id)
這樣:

  def handle_event("start_game", _params, socket) do
    room_id = random_room_id()
    :ets.insert_new(:rooms, {room_id, %Card.Room{id: room_id}})

    {:noreply, push_redirect(socket, to: Routes.game_invite_path(socket, :host, room_id))}
  end

有了連結接著就是做出等待邀請的頁面了
lib/card_web/live/game_live/invite.ex

等待頁面

一不注意就一次打完了... 只好加說明在裡面

defmodule CardWeb.GameLive.Invite do
  use CardWeb, :live_view

  def mount(%{"id" => id} = params, session, socket) do
    # 這邊是拿網址裡面的 id 去 ETS 的 rooms 表格找找看有沒有紀錄,沒有的話就轉到首頁
    # 有的話就在 socket 的 assigns 裡面加入 room, player, invite_path(要邀請 guest 的連結)
    case :ets.lookup(:rooms, id) do
    [{_, room}] ->
      {:ok, assign(socket, %{room: room, player: socket.assigns.live_action, invite_path: Routes.game_invite_url(socket, :guest, id)})}
    _ ->
      {:ok, push_redirect(socket, to: "/")}
    end
  end

  def render(assigns) do
    # 因為剛剛已經有把要用的東西加進 assigns 裡面,這邊就可以直接用 @ 取值
    ~H"""
    <div class="flex flex-col items-center h-screen">
      <header class="text-blue-500 m-4">
        <a href="/" class="text-5xl font-serif">
          ? CardyTotala
        </a>
      </header>
      <div class="flex-grow flex flex-col items-center justify-center">
        <%=# 如果玩家是 host 而且 guest 還沒準備好的話 顯示邀請連結 %>
        <%= if @player == :host && !@room.guest_ready do %>
          <.invite path={@invite_path}/>
        <% end %>
      </div>
    </div>
    """
  end

  def invite(assigns) do
    ~H"""
    <div class="flex flex-col p-4 bg-grey-50 shadow-lg text-center mt-8">
      <div class="bg-blue-300 w-40 h-8 rounded-xl transform skew-x-12 rotate-6 translate-y-6 translate-x-40"></div>
      <div class="transform">
        <label for="invite_url" class="text-2xl font-serif">Guest Link</label><br>
        <input id="invite_url" class="w-96 text-center text-gray-500" type="text" value={@path} readonly>
      </div>
      <p class="text-lg text-gray-600">To begin the game, send this link to your component.</p>
    </div>
    """
  end
end

剛剛上面有 <.invite /> 這種東西,這個是 heex 專用的 component 寫法,不是 html
是 phoenix 1.6 剛加進去的,我們看到另一個方法叫 invite(assigns) 就可以猜到
他就是去執行並 render 裡面的東西取代掉 <.invite />

另外在 heex 裡面不能在html tag 上使用 #{}
以前是這樣

# text_color = "text-red-600"
~L"""
<p class="#{text_color}">
"""

但 heex 不行這樣用

現在要改用

# text_color = "text-red-600"
~H"""
<p class={text_color}>
"""

所以現在邀請頁面長這樣

https://ithelp.ithome.com.tw/upload/images/20211003/201410543hJIXl3Mkz.png


上一篇
19 首頁與開始遊戲按鈕
下一篇
21 "準備完成" 用 PubSub 同步更新網頁
系列文
連線網頁卡牌遊戲(Elixir, Phoenix, Liveview)32
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言