Twitterクローンを作ります #39 チャット機能(1)

前回とりあえずSocket.IOが使えることはわかったので、不要なコードは一旦削除しておきます。


# 画面

チャット画面は以下の3つの画面から構成することにします。

- /messages
→ チャット一覧
- /messages/new
→ チャット作成
- /messages/:chatId
→ チャット画面



とりあえずルートの設定と空の.vueファイルを作っておきましょう。

```javascript:client/src/router/index.js
...
  {
    path: "/messages/new",
    name: "newChat",
    meta: { loginRequired: true },
    component: () => import("../views/NewChatView.vue"),
  },
  {
    path: "/messages/:chatId",
    name: "chat",
    meta: { loginRequired: true },
    component: () => import("../views/ChatView.vue"),
  },
  {
    path: "/messages",
    name: "chats",
    meta: { loginRequired: true },
    component: () => import("../views/ChatsView.vue"),
  },
...
```

# チャット一覧

```vue:client/src/views/ChatsView.vue
<template>
  <div class="uk-margin-right">
    <div class="uk-flex uk-flex-row uk-flex-middle">
      <h3 class="uk-margin-top">メッセージ</h3>
      <span class="uk-flex-1"></span>
      <span uk-icon="plus" class="new-chat"></span>
    </div>
    <div>
      <div class="uk-flex uk-flex-row uk-flex-middle">
        <div
          class="image-container uk-position-relative uk-margin-small-right uk-flex uk-flex-center multi"
        >
          <img
            class="avatar uk-position-absolute"
            src="https://ui-avatars.com/api/?name=?"
          />
          <img
            class="avatar uk-position-absolute"
            src="https://ui-avatars.com/api/?name=?"
          />
        </div>
        <div class="uk-flex-1 uk-flex uk-flex-column uk-width-expand">
          <div class="uk-text-bold uk-text-truncate uk-margin-right">
            チャット名
          </div>
          <div
            class="uk-text-small uk-text-muted uk-text-truncate uk-margin-right"
          >
            最新のチャット
          </div>
        </div>
      </div>
      <hr />
      <div class="uk-flex uk-flex-row uk-flex-middle">
        <div
          class="image-container uk-position-relative uk-margin-small-right uk-flex uk-flex-center"
        >
          <img class="avatar" src="https://ui-avatars.com/api/?name=?" />
        </div>
        <div class="uk-flex-1 uk-flex uk-flex-column uk-width-expand">
          <div class="uk-text-bold uk-text-truncate uk-margin-right">
            チャット名 チャット名 チャット名 チャット名 チャット名 チャット名
            チャット名 チャット名 チャット名 チャット名 チャット名 チャット名
          </div>
          <div
            class="uk-text-small uk-text-muted uk-text-truncate uk-margin-right"
          >
            最新のチャット 最新のチャット 最新のチャット 最新のチャット
            最新のチャット 最新のチャット 最新のチャット 最新のチャット
          </div>
        </div>
      </div>
      <hr />
    </div>
  </div>
</template>

<style scoped>
.image-container {
  width: 40px;
  height: 40px;
}

.avatar {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  border: 2px solid white;
}

.multi .avatar {
  height: 65%;
  width: 65%;
  bottom: 0;
  margin: 0;
}

.multi .avatar:first-of-type {
  top: 0;
  right: 0;
}

.new-chat {
  cursor: pointer;
}
</style>
```

![スクリーンショット 2022-03-31 11.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2536525/2b08a984-7903-ea8d-6a81-1f9392a0e3d3.png)

チャット一覧画面はこんな感じでいいでしょう。

# チャット作成画面

```vue:client/src/views/NewChatView.vue
<template>
  <div class="uk-margin-right">
    <div>
      <h3 class="uk-margin-top">新しいメッセージ</h3>
    </div>
    <div class="search uk-search uk-search-default uk-width-expand">
      <span uk-search-icon></span>
      <input
        type="search"
        class="uk-search-input"
        placeholder="ユーザーを検索"
      />
    </div>
    <div class="selected uk-padding-small">
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
      <span class="uk-label">
        ユーザー１
        <button class="uk-close" uk-close></button>
      </span>
    </div>
    <div class="uk-text-center">
      <button class="uk-button uk-button-primary">チャットを作成</button>
    </div>
    <hr />
    <div>
      <user-panel :user="user" disableLink="true"></user-panel>
      <user-panel :user="user" disableLink="true"></user-panel>
    </div>
  </div>
</template>

<script>
import UserPanel from "@/components/UserPanel.vue";

export default {
  components: {
    UserPanel,
  },
  setup() {
    const user = {
      username: "ユーザー名",
      display_name: "表示名",
    };
    return {
      user,
    };
  },
};
</script>

<style scoped>
.selected {
  min-height: 50px;
}

.search input {
  border: none;
  border-bottom: 1px solid #e5e5e5;
}

.selected .uk-label {
  height: 30px;
  padding-top: 9px;
  margin: 3px;
}

.uk-close {
  color: white;
}
</style>
```

![スクリーンショット 2022-03-31 11.54.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2536525/d0a5bda2-a257-e344-a51a-1d0bf067cc5b.png)

うーん... UIkitは基本的に気に入っているのですが、ラベル的なものがすごく使いづらいですね。

# チャット画面

```vue:client/src/views/ChatView.vue
<template>
  <div class="uk-margin-right uk-flex uk-flex-column uk-height-1-1">
    <div>
      <h3 class="uk-margin-top">チャット</h3>
    </div>
    <div class="uk-text-bold">チャットのタイトル</div>
    <hr class="top-hr" />
    <div class="uk-flex-1 uk-flex uk-flex-column content">
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
      <div>hi</div>
      <div>hello</div>
    </div>
    <hr class="bottom-hr" />
    <div class="uk-flex uk-flex-row uk-flex-middle uk-margin-small-bottom">
      <input type="text" class="uk-input uk-flex-1 uk-margin-right" />
      <span uk-icon="icon: comment; ratio: 1.5"></span>
    </div>
  </div>
</template>

<style scoped>
.content {
  overflow-y: auto;
  margin: 0;
}

.top-hr {
  margin-bottom: 0;
}

.bottom-hr {
  margin-top: 0;
}
</style>
```

![スクリーンショット 2022-03-31 23.00.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2536525/ad05fccc-dc57-4a88-3567-702ec405bb27.png)

チャット画面はやることが多そうなので、とりあえず大枠だけ作っておいて細かい部分はあとでやっていきましょう。

今回はざっくりと画面を作りました。次回はチャットの作成機能を実装していきましょう。