なぽろぐ

気ままに感じたことを記事にまとめます。

自分のイメージキャラクターのイラストを描いていただいたのでホームページ作りました

www.napochaan.me

skeb.jp

ひそなさんに書いていただきました・・・・!!!!最高・・・・・!!!!!

僕がほんとに好きな絵師さんで,どれくらい好きかっていうと毎年カレンダー買ってて今は5年目になります(4年目かもしれない)

マジで目が!!!!!!!!!!!!!!!!!!最高!!!!!!!!!!!!!!!!!!!!!!!

Image from Gyazo

Image from Gyazo

サブスク系の買い物いつの間にかめっちゃ増えてる現象

サブスク系の買い物いつの間にかめっちゃ増えてる現象

クレカの引き落としが毎月きたり,突然年間契約4000円 x 2みたいな出費が起きてびっくりするのでまとめることにしました.

YouTube Premium

¥1180 / month

満足してる.Apple Musicと用途が似てるのでどうにかしたい

YouTubeメンバーシップ

¥490 * 2 / month

必要経費.推しには貢ごう

Apple Music

¥480 / month

学生プランが剥がれないのでまだこの値段で使ってる.次通知がきたら変更する.

YouTube Premiumと用途がかぶってるんだけどCD買った時にクラウド同期できるのが良くて手放せない

Zaim

¥360 / month

ジャパンネット銀行が紐付けられるので自動で家計簿がつけられていい

Bear

¥1500 / year

解約するかも,そんなに画期的なmdエディタじゃなかった

Pixivファンボックス

¥1000 + ¥600 + ¥100 / month

ここは必要経費.必要があればもっと増やす.

vectary

¥1100くらい/month

ARがさくっと作れるジェネレーター.とりあえず課金してる

amazon prime

¥4900 / year

必需品

mac book proの分割払い

¥20000 / month

めっちゃきついけどいい買い物だった

全部(mac book proの分割を除外してる) ¥9640

多分まだあるので見つけ次第追加したり,飽きたら除外する 生活に困ったらいろいろやめる.

開発しているサービスを初めてやってみました

自社サービスをはじめて使ってみた

はじめに

naporitan.hatenablog.com

上のリンクに貼った会社でフロントエンドエンジニアとして仕事をしています.

SOELUというサービスを作っているものの,オンラインフィットネスというタイトルのサービスなので,生粋の陰キャオタクである僕にはサービスを体験してみるハードルがものすごく高く今までサービスを体験せずにいました.いわゆるエアプですね.

ただ,体験しないとサービスをよりよくできないと言うのもあり,社員エンジニアはみんな一度は体験しましょう!みたいなことを言われたので怖いなぁって思いながらも体験してみることにしました.

なぜ怖く感じるのかっていうと,ヨガのインストラクターから自分が見える,しゃべりかけられるって言うのが,陰キャの僕にはすごい怖い.そもそも高専 -> 電通大とかいうオタクしかいない(女性の少ない)環境で育ってきたのでそれはそうなんですが・・・・

ただ,最近ライブビューイング機能*1が実装されたので喋りかけられることはないという安心感があり,今回ばかりは重い腰を上げてレッスンを受けてみることにしました

実際にやってみる

iPhone持ってる人は多分iOSアプリ入れたほうがいいです,Androidアプリに関してはないのでwebでやるしかないですね.

soelu.com

上のリンクからみるといいです.

今無料でできるので.......

ポーズチェックなしのものを選ぶといいと思います,顔も声も見せたくないでしょ?

赤く囲ってあるやつ.下の画像のタグがついてるやつを選ぶといいです

https://i.gyazo.com/7a44e5b61dc28a1ecdea684ec47b0204.png

予約に関してはPCでやって*2実際に時間が来てレッスンを受ける時はPCかiOSアプリでやるのがいい*3と思う.スマホwebはどうなのかよくわからん.エアプなので・・・・

あとは時間きたら

  • webなら マイページ -> レッスン一覧
  • iOSなら マイページ -> 予約リスト

から入ればいい

あとは画面の前の人の言うことを聞いて体動かすだけ・・・・・見られてないから安心して落ち着いてやればいい,先生が喋りかけてくるけど俺たちに喋りかけてるわけじゃない.安心して運動するだけだ〜!!!

終えた感想

引きこもりのエンジニアには結構良さそうだな〜って思いました.

何と言うかいい気分転換になるし体動かすので結構コード書く元気が出たりする.

あと15分だけでもじんわり汗かいた....どんだけ運動してないんだよって話なんですがそれくらいの運動量です.

ただ,エコーっぽいのはある,安いマイク使ってる人とライン電話してるみたいになるけど,いつもVCとかしてエコー慣れしてるオタクなら許容できると思う!!!(許容してね...!)

終わりに

結構良かったので,疲れたな〜って思ったら無料のやつやってみようかなと言う気分になっている.ただ,顔見られるのは怖いし喋りかけられたくないので,ポーズチェックなしっていうタグついただけにするかな〜

是非やってみて,無料なので.

気に入ったら有料会員になってくれてもいいんだよ!!

soelu.com

あとバグ見つけたら教えて〜〜〜〜〜

*1:こちらの声も映像も届かなくなる

*2:僕が作ったので使ってくれ

*3:PCの方が画面大きいからって理由, iOSは使ってみてそんなに不便なかったからって理由

学生ではなくなりました

SOELU株式会社に就職しました

corporate.soelu.com

やってること

Reactでこんな画面作ったりしてます.以下の2つは全て僕が実装したものです.

soelu.com

Image from Gyazo

soelu.com

Image from Gyazo

おわりに

コロナの外出自粛ムードが終わったらご飯でも行きましょう

欲しいものリストです.よければなにか・・・・!! Amazon 欲しいものリスト

aspidaとNestJSでHTTPClientにも型定義しながら開発するぞ!!!!!!!

aspidaとNestJSを使って気持ちがいい開発がしたい!したくない?

今回もお試しリポジトリが存在します.NestJS x aspida x Reactの環境で作っています.是非お試しください

github.com

なにするの?

NestJSはにはSwaggerModuleがありOpenAPIでしゃべることが可能になります.また, aspidaにはopenapi2aspidaというサブライブラリが存在します.この2つの組み合わせによりNestJS -> OpenAPI -> aspidaができるようになりNestJSでControllerを書くと自動的にHTTPClientの型定義が生成されるという感じになります.バックエンドを書くとフロントエンドに必要なコードが生成されるのは気持ちがいいですね.

OpenAPI経由でaspidaの設定ファイルが吐かれるのでNestJSに限らずいろいろは場面で使えるかと思います.今回はその一例としてNestJSを利用しています.

aspidaとは

github.com

HTTPClientに型をつけてくれるライブラリです.GETクエリ, Postパラメータはもちろん, Formデータにも型がつくらしい. しかもURIも保管してくれたりします.

使用感はこんな感じです

今回は /api/user/に問い合わせるとUser オブジェクトが返ってくるようなapiを作りました.

Image from Gyazo

導入方法

yarn add @aspida/axios axios

初期設定だと<root>/apis/以下にエンドポイントと同じ名前のディレクトリを切って設定ファイルを置いていくことになります.この設定ファイルを読んで<root>/apis/$api.tsが作成されます.

aspidaのビルド設定ファイルは公式を見てください aspida/packages/aspida at master · aspidajs/aspida · GitHub

例えば/api/userというエンドポイントが生えていたとすると,次のようにファイルを作って設定を書けばおkです

export interface Methods {
  get: {
    resBody: Types.User
  }
}

作成後に

yarn aspida --build

<root>/apis/$api.tsが作られ型定義されたHTTPClientを使用することができます.

aspidaの設定ファイルは必ずHTTPクライアントをラップする必要があるので,実際使用するときはこのようなutil関数を作っておくと楽だと思います.

import client from "apis/$api";
import aspida from "@aspida/axios";

export const api = client(aspida());

openapi2aspida

OpenAPI形式のjsonをHTTP通信形式で受け取り,aspidaの設定ファイルを自動で作ってくれます.めちゃくちゃ便利.

ルートにaspida.config.jsonを作っておき

module.exports = {
  input: "apis", // outputディレクトリ,
  openapi: { inputFile: "http://localhost:3000/swagger-json" }
}

でおkです.サーバを起動した状態で, openapi2aspida --buildでaspidaの設定ファイルが全て自動生成されます.便利すぎる・・・・

2回目移行ビルドするときはoutputディレクトリ丸ごと消しておく必要があります.

fatal: destination path 'apis' already exists and is not an empty directory.

NestJSについて

Node.jsにおいてバックエンドサーバといえば, Express一強みたいなところがありましたが,最近伸びているバックエンドフレームワークです.Angulerに近い書き心地で様々な機能を後からバシバシ入れることができるプログレッシブフレームワークになっています.Star数も22.5kとかなり多くなってきていて今注目のフレームワークって感じがしてきますね

github.com

今回はNestJSには深く触れず, SwaggerModuleを入れるところから始めていきたいと思います.

2019NestJSのアドベントカレンダーに少し参加したので興味のある人はこちらへ

SwaggerModuleを入れてOpenAPIを喋れるようにする

公式ドキュメントにSwaggerModuleの導入の仕方が書いてあるのでそちらを参考に進めていきます.

docs.nestjs.com

必要なモジュールを入れる

yarn add @nestjs/swagger swagger-ui-express

main.tsを次のように書き換える

import { NestFactory } from "@nestjs/core";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix("api"); // frontendのrenderを/においているのでNestJSで定義される全てのControllerをapi/xxxにしておく

  // 公式とほとんど同じ
  const options = new DocumentBuilder()
    .setTitle("API description")
    .setVersion("1.0")
    .addServer("http://localhost:3000/")
    .build();

  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup("swagger", app, document);

  await app.listen(3000);
}
bootstrap();

ここでaddServerをやっておかないとopenapi2aspidaが実行できないので気をつけましょう.現在これは修正されています

この行で落ちます

 const options = new DocumentBuilder()
    .setTitle("API description")
    .setVersion("1.0")
    .addServer("http://localhost:3000/")
    .build();

これだけでNestJSがOpenAPIを喋れるようになりました.

yarn start:dev

をやってから

特に何もしていなければhttp://localhost:3000/swaggerでこのような画面を見れると思います. Image from Gyazo

また,http://localhost:3000/swagger-jsonでOpenAPIを喋っているのがJSON形式でわかります

なんの型が返ってくるかを明示するためにapp.controller.tsを次のように書き換えます

import { Controller, Get, Post, Body } from "@nestjs/common";
import { AppService } from "./app.service";
import { ApiTags, ApiCreatedResponse } from "@nestjs/swagger";

@ApiTags("AppController")
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  @ApiCreatedResponse({ type: String }) // ここでstringが返ってくることを書いておく
  getHello(): string {
    return this.appService.getHello();
  }
}

これでopenapi2aspidaで型を自動生成する準備は万端です.実際にビルドして使用感を確かめてみましょう

NestJSから吐かれるOpenAPIからaspidaの設定ファイルを生成してみる

NestJSを次のコマンドで起動します

yarn start:dev

http://localhost:3000/swagger-jsonが生きてることを確認し,aspida.config.jsに以下の設定が書いてあれば準備はおkです

module.exports = {
  input: "apis",
  openapi: { inputFile: "http://localhost:3000/swagger-json" }
}

このコマンドを実行すれば自動生成されます

rm -rf apis
yarn openapi2aspida --build

たったこれだけで<root>/apis/$api.tsが生成されました. f:id:Naporitan:20200329084534p:plain

すごいですよね!!! あとはこれをフロントエンドのコードで使うだけ!OpenAPIをメンテしていく意味も出てくるし一石二鳥のライブラリだと思います.

まとめ

去年の12月ごろにも一度使用していたのですが,かなりのアップデートがかかっていてびっくりしました. naporitan.hatenablog.com

NestJSとaspidaを使えばすべてTSの世界でwebの世界を渡り歩けるようになりました.今実戦投入するプロジェクトにこの構成をとっていますが,いい感じです.

副作用として,バックエンドでAPIを書き換えるとき影響範囲がTSの型チェックで落ちたところを修正すればよくなるのでAPI変更の際の精神負荷も少し軽減される気もします.

バックエンドでAPIを作成するとすぐ型安全な状態でそのAPIにアクセスできると言うのは圧巻だと思います. aspida流行れ・・・・!!!

DIVでBlurっぽいことをやる(React)

DIVでもInput要素のblur的なことがしたい!

InputElementは別のところを触るとBlurイベントを発火してフォーカスが外れます.

developer.mozilla.org

MDNのサイトで動作は確認できるかと思います.

これをDIV Elementでもやりたいなぁと言うのが今回の議題です.

Image from Gyazo

今回はBlur的なことをしたくなったSelectorUIを用意しました.https://naporin0624.github.io/napoblog-assets/#/div-blur

  • 上のセレクターを出すボタンを押すとセレクターを表示する
  • 要素を選択するところを触っている時はセレクターを消さない
  • 背後の要素はスクロールできる
  • それ以外のところを触るとセレクターが閉じる

ようなUIを考えます.

方針

セレクターUIはdivの中に存在するようになっていて,次のようなDOM構成になっています.

  • ContainerDIV
    • ButtonDIV
    • MenuDIV
      • OptionGroupDIV
        • OptionDIV
        • OptionDIV
        • ...

ButtonDIVもOptionGroupDIVもOptionDIV触っても親の要素は常にContainerDIVになるので

developer.mozilla.org

こちらのclosestメソッドを使ってクリックされた要素の親にContainerがあるかどうかでフォーカスがはずれたかどうかを検知するようにしていきます!関係ないところをクリックしてもその親にはContainerはいないのでフォーカスは外れる仕組みです!

実装

こんな感じでできるかと思います!InputElementが自分でblurイベントを発火してくれるところを自分でclosestメソッドを使ってblurイベント相当のものを生成しています.

function Selector() {
  // closestにSelectorをわたすためにContainerのrefをとります
  const ref = useRef<HTMLDivElement>(null);
  useEffect(() => {
    const handler = (e: Event) => {
      // Blurを制御したい親Elementのselector(クラス)refからを作る
      const selector = ref.current?.className
        .split(" ")
        .map(s => `.${s}`)
        .join();

      // eはクリックしたところから発火されるイベント
      // eをclosestで比較し,eの親にrefが含まれるか確認する
      const blur = !(e.target as HTMLElement).closest(selector || "");

      // 親にrefが含まれていなければフォーカスを外す
      if (blur) setIsFocus(false);
    };

    document.addEventListener("click", handler);
    return () => document.addEventListener("click", handler);
  }, []);

  return (
    <Container ref={ref}>
      <Button></Button>
      <Menu>
        <OptionGroup>
          <Option>hoge</Option>
          <Option>huga</Option>
          <Option>nyan</Option>
        </OptionGroup>
      </Menu>
   </Container>
  )
}

ここなんですが

const selector = ref.current?.className
        .split(" ")
        .map(s => `.${s}`)
        .join();

javascriptだと

const selector = [...ref.current.classList].map(s => `.${s}`).join()

みたいなことができたんですけど,TSだと怒られましたw

最後に

今回使ったプログラムはここに置いてあります!必要であれば使ってください. github.com

自作のUI(DIVで作ったやつ)でフォーカス制御してみたいなことがあって困っていろいろ模索した結果こんな感じの解にたどり着きました.MDNじろじろ見る癖が少しづつついてきたかなぁって感想と,CSS筋が前よりついてきた気がします.

もっとこうしたらいいよとかあれば@naporin24690に教えてください〜!

LottieWebでチェックボックスの状態を管理する

Lottieで消えちゃうCheckBox作るぞ!

こういうのを作りたい

https://naporin0624.github.io/napoblog-assets/#/

Image from Gyazo

今回はこちらのチェックボックスアニメーションを使わせていただきました.

lottiefiles.com

コードの全体像

Lottieのマウントにはreact-lottieを使っています.animを直で触りたいのでそこの部分だけ型定義を拡張して使っている感じですね.

github.com

index.tsx

import React, { useRef, useEffect } from "react";
import Lottie from "react-lottie";
import checkbox from "./checkbox.json";
import { AnimationItem } from "lottie-web";

interface LottieType extends Lottie {
  anim: AnimationItem;
}
interface Props {
  checked: boolean;
}
const options = {
  loop: false,
  autoplay: false,
  animationData: checkbox,
  rendererSettings: {
    preserveAspectRatio: "xMidYMid slice",
  },
};
export const CheckBox = (props: Props) => {
  const { checked } = props;
  const ref = useRef<LottieType>(null);
  const prev = useRef<boolean>();

  useEffect(() => {
    // 初回マウントときはレンダリングしない
    // falseでは行ってきたときにgoToAndPlayされるのを防ぐため
    if (prev.current === undefined) {
      prev.current = checked;
      // 初回マウント かつ チェックボックスが選択されているならフレームの最後からアニメーションを実行する
      checked && ref.current?.anim.goToAndPlay(ref.current?.anim.getDuration(true), true);
      return;
    }

    if (checked) {
      ref.current?.anim.goToAndPlay(0, true);
    } else {
      ref.current?.anim.setDirection(-1);
      ref.current?.anim.goToAndPlay(15, true);
    }
  }, [checked]);
  return <Lottie ref={ref} options={options} width={300} />;
};

最後に

lottie-webにはここでフレームを止めるみたいなことができないぽいのでこのような措置を取りました.もっといい方法があれば教えてください🙇‍♂️

初めはこの子

lottiefiles.com

で実装していてチェックされてから消えるまでセットのアニメーションだったために以下のようにしていたのですが

  useEffect(() => {
    ref.current?.anim.playSegments([30, 75], true);
    ref.current?.anim.stop();
  }, []);

なぜかこうなる・・・・・・

Image from Gyazo

消すとうまくいくので明らかにplaySegmentが悪いのはわかっているんですけど,どう直したらいいのかわからなかったので上で紹介したアニメーションを使いました

有識者教えてください!!!!

わかっていること

  • destroyの前に動き始めている
    • useEffectのreturnにconsole.logをしこませて検証
  • playSegmentの上にconsole.logをおいても初期レンダリング以外では反応なし

ぐらいです・・・・・・