スポンサーリンク

ElmでFirebase認証とFirestoreを写経してみる

2019年6月19日

ElmとFirebaseでTODOアプリを作りたいのですが、とりあえず、以下のサイトを写経してみたいと思います。

https://qiita.com/ababup1192/items/f27f9af282d9fa642eb5

スポンサーリンク

開発環境

Windows 10 Pro
Chrome
VisualStudioCode 1.32.3
git version 2.20.1.windows.1
nvm 1.1.7
node 10.2.0
npm 6.4.1
elm 0.19.0-bugfix6
elm-format 0.8.1
VisualStudioCodeの拡張機能でelmをインストールして、settings.jsonに以下のようにelmを設定。
(『Alt + Shift + F』と『Ctrl + S』を使用。)

    "[elm]": {
        "editor.formatOnSave": true
    },

新規Firebaseプロジェクトを作成

Googleアカウントがなければ作成し、

https://console.firebase.google.com/

へログインして、新規プロジェクトを作成します。
今回は、firestore-auth という名前にしました。

下の方のチェックボックスをONにして、『プロジェクトを作成』をクリックします。

以下のような画面になります。

FirebaseでGoogle認証を有効にする

Google認証ができるように設定します。以下の2つのことを行います。

  • プロジェクトでGoogle認証を有効にする
  • 特定のアドレスからGoogle認証を有効にする(承認済みドメインの設定)

まず、画面左上の方の、『Authentication』をクリック

『ログイン方法』をクリック

Google をクリック

次のような画面になるので、『有効にする』をONにして、プロジェクトのサポートメールのところをクリックして、登録されたGmailアドレスを選択します。(私の場合は1個しか選択できませんでした。)

画面右下の『保存』をクリック。

以上で、このプロジェクトでGoogle認証が有効になりました。

特定のアドレスからGoogle認証を有効にする(承認済みドメインの設定)(localhostからはデフォルトで有効になっています。)方法は、またデプロイ先のホームぺージアドレスが決定してから、設定したいと思います。

Firestoreデータベースの初期設定

Firebaseのデータベースには

  • Realtime Database
  • Firestore

の2つがありますが、今回は、Firestoreを用います。

画面の左上の方の、『Database』をクリックします。

Cloud Firestoreの『データベースの作成』をクリック。

そのまま、『有効にする』をクリック

参考: https://qiita.com/sgr-ksmt/items/1a731fdadf06119d35fc
@sgr-ksmt
2018年12月12日に更新
Firestore rules tips

Firebaseで認証されたアカウントからのみ、Firestoreのデータベースをread & writeできるように、Cloud Firestoreセキュリティールールを変更します。

画面上の方の、『ルール』をクリックします。

以下のような画面になるので、

allow read, write: if false;

を以下のように書き換えます。

allow read, write: if request.auth != null;

(変更前)

(変更後)

その後、図のように、『公開』をクリックします。

後で、アプリからデータベースがどのように変更されたかを見るときは、『Database』>『データ』の順にクリックしていくと、見ることができます。

FirebaseをWEBアプリから利用するための初期設定

画面左上の『Project Overview』をクリック。

図のように、『</>』ボタンをクリックします。

アプリのニックネームに適当な名前を入力し、Firebase HostingのチェックボックスはOFFのまま、『アプリを登録』をクリック。

今回は、elm-foo というニックネームにしました。

以下のような画面になるので、グレーの部分の中身を、メモ帳などにテキストファイルとしてコピーしておきます。後で、Elmアプリの一部に記載します。

新規create-elm-appアプリの作成

C:/elm/ フォルダをVisualStudioCodeで開き、Ctrl+@でターミナル画面を開き、以下を入力します。

npm install create-elm-app -g
create-elm-app elm-firestore-foo
cd elm-firestore-foo
elm-app start

create-elm-app@3.0.6 がインストールされました。

参考: https://github.com/halfzebra/create-elm-app

public/index.htmlの編集

public/index.htmlの<body>タグ内の後ろのほうに以下を入力します。(上記のFirebaseのグレーの部分の一番上の部分)

<!-- The core Firebase JS SDK is always required and must be listed first -->
<script src="https://www.gstatic.com/firebasejs/6.0.2/firebase-app.js"></script>

(変更前)

(変更後)

この後、なんとなく、コピペ元の、以下に変更しました。

<script src="https://www.gstatic.com/firebasejs/5.8.3/firebase.js"></script>

蛇足ですが、bulmaとFontAwesomeを使用したい場合は、
http://i-doctor.sakura.ne.jp/font/?p=36580 をご覧ください。

src/index.jsの編集

引き続き、 https://github.com/ababup1192/elm-firebase/blob/master/src/index.js を写経していきます。

src/index.jsに、firebaseConfigや、elm用のportなどを記載していきます。

const config = {} の中身は、上記のFirebaseの初期設定でのグレーの部分のご自身のアプリのデータをコピペしてください。

const app = Elm.Main.init();

const config = {
  apiKey: "<YOUR-API-KEY>",
  authDomain: "<YOUR-APP>.firebaseapp.com",
  databaseURL: "https://<YOUR-APP>.firebaseio.com",
  projectId: "<YOUR-APP-ID>",
  storageBucket: "<YOUR-APP>.appspot.com",
  messagingSenderId: "<YOUR-APP-SENDERID>"
};
firebase.initializeApp(config);
const provider = new firebase.auth.GoogleAuthProvider();
const DB = firebase.firestore();

// ログイン監視
firebase.auth().onAuthStateChanged((user) => {
  if (user) {
    app.ports.signedIn.send(true);

    // ログイン後にDatabase監視
    DB.collection('foo').doc(user.uid).onSnapshot((doc) => {
        const data = doc.data();
        app.ports.read.send(data.input);
    });
  }
});

app.ports.signIn.subscribe(_ => {
  firebase.auth().signInWithPopup(provider).then((_) => {}).catch((error) => {});
});

app.ports.push.subscribe(text => {
  const user = firebase.auth().currentUser;
  DB.collection('foo').doc(user.uid).set({input: text});
});

(変更前)

(変更後)

src/Main.elmの編集

MODEL, UPDATE, VIEWの順に写経していきたいと思います。変更前は、以下のようになっています。

portの追加

最初の方を、以下のように書き換えます

port module Main exposing
    ( Model
    , Msg(..)
    , init
    , main
    , read
    , signedIn
    , update
    , view
    )

import Browser
import Browser.Navigation as Nav
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http exposing (Error(..))
import Json.Encode as E



-- ---------------------------
-- PORTS
-- ---------------------------


port signIn : () -> Cmd msg


port push : String -> Cmd msg


port read : (String -> msg) -> Sub msg


port signedIn : (Bool -> msg) -> Sub msg

elm/json パッケージをインストールします。VisualStudioCodeのターミナル画面(Ctrl+@で表示)で、以下を入力します。

elm install elm/json

elm/http パッケージをインストールします。VisualStudioCodeのターミナル画面(Ctrl+@で表示)で、以下を入力します。

elm install elm/http

その後、一度、Ctrl+Cでサーバを停止して、elm-app startでサーバを起動すると、以下のようになります。

MODELの編集

今回必要なMODELは、以下の2つとなりますので、それらを記載します。

  • 入力するテキストpushText
  • Google認証でサインインしているかどうかisSignedIn
---- MODEL ----


type alias Model =
    { pushText : String
    , isSignedIn : Bool
    }


init : () -> ( Model, Cmd Msg )
init isSignedIn =
    ( { pushText = "", isSignedIn = False }, Cmd.none )

PROGRAMの編集

subscriptionsでindex.js(Javascript)からの"read"と"signedIn"ポートを受け取るために、以下のように変更します。

---- PROGRAM ----


main : Program () Model Msg
main =
    Browser.element
        { init = init
        , update = update
        , view = view
        , subscriptions = subscriptions
        }


subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.batch [ signedIn SignedIn, read Read ]

(変更前)

(変更後)

UPDATEの編集

よくわかっていないけど、とりあえず写経です。Modelを考えるのは比較的簡単ですが、Msgは、自分で思いつける気がしません。。。

とりあえず今回のMsgとしては、以下の4つのようです。

  • SignIn : ElmからFirebaseにサインインの申し込みをする(?)
  • Push  : Elmで入力したテキストをFirebaseに送る
  • Read  : FirebaseのテキストをElmで受け取る
  • SignedIn : FirebaseからサインインできたことをElmで受け取る

これがよくportのところで説明されている、

『 Elm と JavaScript は、ポートを通じて互いに一方的に送信を行うことで、通信をすることができる 』

っていうやつなんでしょうか。。。

---- UPDATE ----


type Msg
    = SignIn
    | Push String
    | Read String
    | SignedIn Bool


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        SignIn ->
            ( model, signIn () )

        SignedIn isSignedIn ->
            ( { model | isSignedIn = isSignedIn }, Cmd.none )

        Push text ->
            ( { model | pushText = text }, push text )

        Read text ->
            ( { model | pushText = text }, Cmd.none )

(変更前)

(変更後)

VIEWの編集

見栄えを調整しようとすると心が折れるので、また後で、、、

---- VIEW ----


view : Model -> Html Msg
view { pushText, isSignedIn } =
    div [ class "container" ]
        [ img [ src "/logo.svg" ] []
        , h1 [] [ text "Your Elm App is working!" ]
        , button [ onClick SignIn ] [ text "Google サインイン" ]
        , if isSignedIn then
            input [ type_ "input", value pushText, onInput Push ] []

          else
            text ""
        ]

(変更前)

(変更後)

いろいろ調整

Googleサインイン ボタンをクリックしてみると、ログインできるでしょうか?

ログインすると、Googleサインイン ボタンの右側にINPUTボックスが現れて、、、を期待していましたが、そう甘くはなく、以下のようなエラーになってしまいました。

Firestoreを見てみます。

試しに、『コレクションを追加』で、foo を追加してみます。

自動ID をクリックしてから、保存 をクリックします

これでもダメでした。

その後、FirebaseのAuthenticationからログインユーザの『ユーザーUID』をコピーして、foo コレクション下のdocument名として登録し、さらにその下に input: “foo2″ と入れて、ようやくアプリが動きましたが、これは明らかにおかしい動き、、、firebaseのバージョンの5と6の違い、もしくは、Browser.documentをBrower.elementで代用したことが原因にあるのかもしれませんが、よく分かりません。。。

(2019/5/11追記)developmentモードでは、赤いエラーが出るのですが、エラーの右上の×ボタンをクリックすると、普通にいけました。create-elm-appをNetlifyにデプロイする方法はやや面倒(参考: http://i-doctor.sakura.ne.jp/font/?p=36506 )ですが、PWA(Progressive Web Apps)として、スマホでサイトを訪問すると、「ホーム画面に追加」が出てきて、すぐにインストールすることができます。

DEMOサイト(create-elm-app版): https://zen-thompson-04a101.netlify.com/

---- VIEW ----


view : Model -> Html Msg
view { pushText, isSignedIn } =
    div []
        [ nav [ class "navbar has-background-info" ]
            [ div [ class "navbar-brand" ]
                [ a [ class "navbar-item has-text-white", href "#" ] [ text "elm firestore foo" ]
                ]
            ]
        , section [ class "section" ]
            [ div [ class "container" ]
                [ img [ src "/logo.png" ] []
                , br [] []
                , a [ class "button is-success", onClick SignIn ] [ text "Google サインイン" ]
                , br [] []
                , div [ class "columns is-centered" ]
                    [ div [ class "column is-half" ]
                        [ if isSignedIn then
                            input [ class "input", type_ "input", value pushText, onInput Push ] []

                          else
                            text ""
                        ]
                    ]
                ]
            ]
        , footer [ class "footer" ]
            [ div [ class "content has-text-centered" ]
                [ p []
                    [ a [ href "http://i-doctor.sakura.ne.jp/font/?p=37747" ] [ text "WordPressでフリーオリジナルフォント2" ]
                    ]
                ]
            ]
        ]

うまくいかないので、元のソースコードをダウンロードして、FirebaseConfigのみ書き換えて実行

(2019/5/11追記) 当初はcreate-elm-app版はあきらめて、 以下のように実行してみました。

https://github.com/ababup1192/elm-firebase
からZIPファイルをダウンロードして、src/index.jsの
const config = { }
の中身だけ自分のFirebaseのconfigに変更して、
npm start
したら、思った結果になりました、、、が、原因はよく分からず。。。

Netlifyにデプロイ

BitBucketにgit pushして、BitBucket経由でNetlifyにデプロイします。

DEMOサイト:https://cranky-hugle-2eb592.netlify.com/

このまま Googleサインイン をクリックしてもうまくいかないので、
https://console.firebase.google.com
において、Authentication > ログイン方法 で、『承認済みドメイン』に、上記アドレスを追加します。

すると、上記のnetlifyにデプロイしたWEBアプリ上からも、Google認証ができるようになります。

上記のように文字を入力すると、ちゃんと、Firestoreにも登録されているようです。

なお、こちらの方は、デフォルトではPWA化はされていません。(少し頑張れば、PWA化できるのだとは思いますが、、、)

今回写経したサイト

https://qiita.com/ababup1192/items/f27f9af282d9fa642eb5

Elmでお勧めのサイトと本

https://guide.elm-lang.jp/
Elm公式サイトの日本語訳です。