マッチングアプリライクなニュース機能を作ってみた

こんにちは、@natsuya_ITと申します。

先日、 マッチングアプリのデート体験を共有するSNSアプリ - マッチシェアに**スワイプでニュースを閲覧できる機能**を追加したので軽く紹介したいと思います 🦜

👇リリース中のアプリはこちら👇

https://matching-share-introduction.vercel.app


## 作ったもの

<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689520/2177db89-d157-8000-87ff-7665639042c0.gif">

こちらが今回作ったニュース機能です 📗
機能としては非常にシンプルでニュースカードを右へとスワイプすると既読、左へスワイプすると未読のままに、カード押下でニュースページに遷移するといったUXにしています。


## 実装

### クライアント

上記ニュースのスワイプUIは[rn-swiper-list](https://github.com/Skipperlla/rn-swiper-list)というパッケージを参考に、アプリで利用しやすいように少しカスタマイズして自前で実装しています。

https://github.com/Skipperlla/rn-swiper-list

#### 主要なpropsとその説明

<table>
  <thead>
    <tr>
      <th>props</th> <th>説明</th>
    </tr>
  </thead>
  <tr>
    <td> data </td> <td>カードのレンダリングに使用されるデータ オブジェクトの配列</td>
  </tr>
  <tr>
    <td> renderCard </td> <td>カードをレンダリングする関数</td>
  </tr>
  <tr>
    <td> cardStyle </td> <td>各カードに適用されるスタイル</td>
  </tr>
  <tr>
    <td> children </td> <td>コンポーネント内に表示される子コンポーネント</td>
  </tr>
  <tr>
    <td> onSwipeRight </td> <td>カードを右にスワイプしたときに呼び出される関数</td>
  </tr>
  <tr>
    <td> OverlayLabelRight </td> <td>右スワイプのオーバーレイ ラベルとしてレンダリングされるコンポーネント</td>
  </tr>
</table>

使い方は非常に簡単で下記のようにSwiperコンポーネントに必要なpropsを渡してあげるだけです。

```jsx
import { Swiper, type SwiperCardRefType } from 'rn-swiper-list';
// (中略)
return(
    <Swiper
      ref={ref}
      cardStyle={styles.cardStyle}
      data={IMAGES}
      renderCard={renderCard}
      onSwipeRight={(cardIndex) => {
        console.log('右にスワイプしたよ', cardIndex);
      }}
      OverlayLabelRight={OverlayLabelRight}
      // (省略)
    />
)
```

#### スワイプ周りの実装

初期表示はニュースを15件取得しており、最後の1件をスワイプした時に、リクエストのpageをインクリメントし追加のニュースを取得するというシンプルなものです。

```typescript
const setIsLoading = useSetRecoilState(getLoadingAtom('getNewsList'));

const handleGetNews = useCallback(
    async (page: number) => {
      setIsLoading(true);
      const res = await axios.get("https://XXXXXX.XXX", {
        params: hoge,
        headers: hoge,
      });

      setCurrentNewsList(res.data.hoge);
      setIsLoading(false);
      // （以下省略）
    },
    [setCurrentNewsList, setIsLoading],
  );

```


```typescript
// 右スワイプ（既読）した時に呼ばれる関数
const handleReadNews = async (newsId: string) => {
    // 既読apiを呼ぶ
    const res = await handleReadNewsTrigger({
      params: hoge,
    });
    
    //（中略）
    
    // スワイプしたニュースを取り除く
    handleRemoveNews(newsId);
    // 追加ロードが必要な場合はpageをインクリメントして新しいニュースを取得する
    if (shouldFetchNextNewsList) {
      handleGetNews(currentNewsListPageIndex + 1);
      setCurrentNewsListPageIndex(currentNewsListPageIndex + 1);
    }
}
```


### サーバー

サーバー側ではpythonを使用して、マッチングアプリに関連する記事を[PR TIMES](https://prtimes.jp/)からスクレイピングし、DBに取得した情報を増分だけ格納するようにしています 🐰

また、都度最新のニュースを手動で取得することは手間なので、[Jenkins](https://www.jenkins.io/)を使用して、1日に1回スクレイピングを行い自動化するようにしました。



## 💃 終わりに


マッチシェアに導入したニュース機能を紹介してみました。

初めはマッチングアプリのようなスワイプできるUI/UXをどのように実装しようか少し悩みましたが、個人的には**rn-swiper-list**は非常に使いやすい印象を受けました。

今回はニュース機能の実装に使用しましたが、公式レポジトリのexampleでも紹介されているように、マッチングアプリを開発するなどする時にReact Nativeを使用するのであれば、導入を検討してみるのもアリだと感じました。


またサーバー側でスクレイピング作業を自動化したことでユーザー数に頼らない、アプリの動的な機能を実装することができました。


今回追加したニュース機能についてやその他質問等あればご質問お待ちしております 🍭
開発メンバーや開発の背景、技術構成等は[こちら](https://qiita.com/natsuya-kume/items/2d34fb761e0f27068763)の記事で紹介しておりますので、興味がある方は見てみてください🕺


[マッチシェア iOS](https://apps.apple.com/jp/app/%E3%83%9E%E3%83%83%E3%83%81%E3%82%B7%E3%82%A7%E3%82%A2-%E3%83%9E%E3%83%83%E3%83%81%E3%83%B3%E3%82%B0%E3%82%A2%E3%83%97%E3%83%AA%E5%B0%82%E7%94%A8%E3%81%AEsns/id6468022331)

[マッチシェア Android](https://play.google.com/store/apps/details?id=com.matchingShare.matchingShare&pli=1)