Monaca + NCMBでTwitter風アプリを作る（その3：プロフィール画面とフォローの実装）

NCMBでは公式SDKとしてSwift/Objective-C/Kotlin/Java/Unity/JavaScript SDKを用意しています。また、それ以外にもコミュニティSDKとして、非公式ながらFlutter/React Native/Google Apps Script/C#/Ruby/Python/PHPなど幅広い言語向けにSDKが開発されています。

今回は公式SDKの一つ、JavaScript SDKとFramework7を使ってTwitter風のアプリを作ってみます。[前回はログイン周りについて解説しました](https://qiita.com/goofmint/items/bd7cb86663e303f90399)ので、今回はプロフィール画面とフォロー機能を解説します。

## **途中のコード**

まだ完成にはほど遠いですが、プロフィールとフォロー/フォロワーの仕組みまで実装した分を [NCMBMania/monaca-social-app](https://github.com/NCMBMania/monaca-social-app) にアップロードしてあります。

## **利用技術について**

今回は次のような組み合わせになっています。

- Monaca
- Framework7
- NCMB

## プロフィール画面を表示するURL

まずフォロー画面を表示するURLを `js/routes.js` に定義します。 `/users/:userName/` というのがそうです。これで `/users/goofmint/` のような形でプロフィール画面が表示されます。

```jsx
const routes = [
  {
    path: '/',
    url: './index.html',
  },
  // ホーム画面
  {
    path: '/home/',
    componentUrl: './pages/home.html',
    beforeEnter,
  },
  // プロフィール画面
  {
    path: '/users/:userName/',
    componentUrl: './pages/profile.html',
  },
  // Default route (404 page). MUST BE THE LAST
  {
    path: '(.*)',
    url: './pages/404.html',
  },
];
```

## プロフィール画面について

`pages/profile.html` のHTMLです。 `userProfile` はユーザプロフィールオブジェクトです。 `mine` は自分の画面かどうかのフラグ、 `followed` はフォロー済みかどうか判断するフラグです。

イベントとしてはフォローする際の `followUser` 関数と、 フォローを解除する `unfollowUser` 関数があります。

```jsx
<template>
  <div class="page">
    <div class="navbar">
      <div class="navbar-bg"></div>
      <div class="navbar-inner sliding">
        <div class="left">
          <a href="#" class="link back">
            <i class="icon icon-back"></i>
            <span class="if-not-md">戻る</span>
          </a>
        </div>
        <div class="title">${userProfile.userName}さんのプロフィール</div>
      </div>
    </div>
    <div class="page-content">
      <div class="block" style="text-align: center;">
        <img class="profileImage" src="${userProfile.image}" />
      </div>
      <div class="block block-strong no-hairlines text-center">
        ${userProfile.userName}
      </div>
      <div class="block block-strong">
        <p class="row">
          ${ mine ?
            $h`<button class="col button button-small disabled button-fill">フォローする</button>` :
            followed ? 
            $h`<button class="col button button-small button-fill" @click=${unfollowUser}>フォロー中</button>` :
            $h`<button class="col button button-small button-fill" @click=${followUser}>フォローする</button>`
          }
        </p>
      </div>
      <div class="block no-hairlines">
        <p innerHTML="${marked.parse(userProfile.profile || '')}"></p>
      </div>
    </div>
  </div>
</template>
```
![0932 localhost - 0318220022.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/72d2ec5e-e83e-f846-512d-51a0978ff39e.jpeg)


## 変数の準備

まず上記画面で必要な変数を用意します。 `/users/:userName/` とルーティングを定義した場合、 `props.userName` でその値が受け取れます。その値を使って、そのユーザーのユーザープロフィールを取得します。ユーザープロフィールのobjectIdが自分のものと同じならば、自分のデータであると判別できます。

```jsx
export default async function (props, { $store, $f7, $on, $update }) {
  // URLパラメータからユーザー名を取得
  const { userName } = props;
  // ストアから自分のプロフィールとフォローを取得
  const { profile, follow } = store.getters;
  // ユーザー名からプロフィールを取得
  const userProfile = await getUserProfile(profile, userName);
  // 自分のプロフィールかどうか判断
  const mine = profile.value.objectId === userProfile.objectId;
  // フォローしているかどうか判断
  let followed = (follow.value.follows || []).indexOf(userProfile.objectId) >= 0;
  // 略
}
```

`getUserProfile` 関数は該当ユーザーのユーザープロフィールオブジェクトを取得する関数です。ただし、ユーザー名が自分のものだった場合は自分のユーザープロフィールを返却して、APIコール数を減らしています。

```jsx
// ユーザー名からプロフィールを取得する関数
const getUserProfile = (profile, userName) => {
  // 自分のプロフィールの場合はストアから取得
  if (profile.userName === userName) {
    return profile.value;
  }
  const UserProfile = ncmb.DataStore("UserProfile");
  return UserProfile
    .equalTo("userName", userName)
    .fetch();
};
```

これで画面の情報は揃った状態です。

## イベントについて

今回、イベントは2つあります。

1. フォロー
2. フォロー解除

### フォロー

フォロー用のイベントは次の通りです。 `follows` カラムに `addUnique` 関数を使って対象となるユーザーのプロフィールのIDを追加します。 `addUnique` を使うことで、重複なく追加できます。

```jsx
// フォローする関数
const followUser = async () => {
  // Followオブジェクトの有無を確認
  await follow.value
    .addUnique("follows", userProfile.objectId)
    .update();
  // フォロー状態を更新
  followed = true;
  // 描画を更新
  $update();
};
```

### フォロー解除

フォロー解除用のイベントは次の通りです。 `follows` カラムに `remove` 関数を使って対象となるユーザーのプロフィールを削除します。 `remove` を使うことで、現在のフォロー状態を気にすることなく該当データを削除できます。

```jsx
// フォローを解除する関数
const unfollowUser = async () => {
  await follow.value
      .remove("follows", userProfile.objectId)
      .update();
  // フォロー状態を更新
  followed = false;
  // 描画を更新
  $update();
};
```

![0931 localhost - 0318220018.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/a4ff5f98-8723-57ca-d243-1839eb65ca6f.jpeg)


## まとめ

これでプロフィール画面と、フォロー/フォロー解除が完成です。次回はスクリプト機能を使って、フォロー数とフォロワー数を更新します。