21 "准备完成" 用 PubSub 同步更新网页

拉出 component

Component 除了在同一个 module 用之外也能拉出来放
我们来把 logo 拉出来到 lib/card_web/component.ex

defmodule CardWeb.Component do
  use Phoenix.Component

  def logo(assigns) do
    ~H"""
    <header class="text-blue-500 m-4">
      <a href="/" class="text-5xl font-serif">
        ? CardyTotala
      </a>
    </header>
    """
  end
end

我要用到的时候我可以用

<CardWeb.Component.logo />

如果我有在要用的地方 import CardWeb.Component 的话,
我就可以直接使用

<.logo />

我感觉等待画面需要有一个大家有没有准备好的状态显示

  def ready(assigns) do
    ~H"""
    <div class="flex flex-col p-4 text-center mt-8">
      <div class="bg-blue-300 w-32 h-8 rounded-xl transform skew-x-12 -rotate-6 translate-y-6 translate-x-20"></div>
      <h2 class="transform text-2xl font-serif">Room status</h2>
      <div class="flex justify-between mt-2 w-60">
        <.ready_status player="Host" ready={@room.host_ready}/>
        <.ready_status player="Guest" ready={@room.guest_ready}/>
      </div>
      <%= unless Map.get(@room, :"#{@player}_ready") do %>
        <div class="flex justify-between w-40 mx-auto mt-8">
          <div class="text-lg text-center">Are you ready?</div>
          <div class="h-8">
            <div class="bg-green-300 w-8 h-8 rounded-xl transform skew-x-12 -rotate-45 translate-x-2"></div>
            <button class="transform -translate-y-7 text-xl">Yes</button>
          </div>
        </div>
      <% end %>
    </div>
    """
  end

  def ready_status(assigns) do
    ~H"""
    <div class="flex">
      <%= @player %>:
      <%= if @ready do %>
        <div class={"ml-8 bg-green-300 w-6 h-6 rounded-3xl transform -skew-x-3 rotate-12"}></div>
      <% else %>
        <div class={"ml-8 bg-red-300 w-6 h-6 rounded-3xl transform -skew-x-3 rotate-12"}></div>
      <% end %>
    </div>
    """
  end

画面在 host 与 guest 的状态
https://ithelp.ithome.com.tw/upload/images/20211004/20141054e5jlM8NP8T.png

PubSub

在玩家按下 准备好 按钮的时候,这次不只要更新画面上的状态,还要跟对手同步

要做的事情有

  1. 接收按钮准备好按钮
  2. 储存更新後的数值
  3. 广播更新後的数值
  4. 接收广播

感觉要包一些进 room.ex

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

  # 订阅某个 room_id 频道
  def subscribe(id) do
    Phoenix.PubSub.subscribe(Card.PubSub, id)
  end

  # 广播 room 物件至某个 room_id 
  def broadcast(id, room) do
    Phoenix.PubSub.broadcast(Card.PubSub, id, room)
  end

  # 从 ETS 表里面用 room_id 找 room ,没找到就回 nil
  def get(id) do
    case :ets.lookup(:rooms, id) do
      [{_, room}] -> room
      _ -> nil
    end
  end

  # 在 ETS 表上更新指定 room ,顺便广播
  def update(id, room) do
    :ets.insert(:rooms, {id, room})
    broadcast(room.id, room)
  end
end

这样子 mount 那边要拿 room 的时後也要记得改用 get

把上面那些方法写好应该就很快了

  1. 接收按钮准备好按钮
    加上 phx-click
  <button phx-click="ready" class="transform -translate-y-7 text-xl">Yes</button>

接收 ready 事件

  def handle_event("ready", _params, %{assigns: %{player: player, room: room}} = socket) do
    room =
      socket.assigns.room
      |> Map.replace(:"#{player}_ready", true)

# 2. 储存更新後的数值
    Room.update(room.id, room)

    {:noreply, socket}
  end
  1. 广播更新後的数值
    写在update里了
  2. 接收广播

收到新的 room 就更新就好了

  def handle_info(room, socket) do
    {:noreply, assign(socket, :room, room)}
  end

状态改了就好了

LiveView 这样统一状态超赞的,画面自己就会跟着变了
动图:
https://ppt.cc/f7bEwx@.gif


<<:  Day 23 : 集成式学习

>>:  Day 20: 人工智慧在音乐领域的应用 (AI作曲-基因演算法三 突变)

Day 20 例外和中断机制的定义

大多数的嵌入式处理器会提供例外(exception)和中断(interrupt)这两个功能,允许处理...

实施零信任架构以防止横向移动,XACML最不可能进行身份验证

-示例 XACML 实现 XACML 旨在支持授权,而不是身份验证。 XACML 代表“可扩展访问...

D-21 委派 ? delegate ? Action ? Linq

物件导向之後呢 小光跟着大头从最基础的基本语法学习到方法以及物件导向,那接下来要怎麽让开发的速度更快...

Day 20 api介绍

到了 Odoo 8.0,引入了新API,封装在api.py文件中,主要有一下几种类型: Odoo 1...

从零开始学游戏开发:入门程序实作 Part.7 重新计分

这是 Roblox 从零开始系列,入门章节的第十三个单元,我们的游戏出现Bug了,那就是死亡後分数还...