スポンサーリンク

ElmとFirebaseでチャットアプリを写経してみる(2)複数ファイルを1個のファイルにする

2019年6月16日

ElmとFirebaseでチャットアプリを作成したいシリーズの2回目。前回は、
https://github.com/qnoyxu/chat-room
のコードを実行し、Netlifyにデプロイしてみたのですが、自分ではそのコードを変更することが困難なので、まずは、複数ファイルを1個のファイルにしてみたいと思います。

https://qiita.com/qnoyxu/items/ee479b2b96831907e024

スポンサーリンク

今回やりたいこと

ソースコード
(index.htmlの55行目のFirebaseのアドレスはご自身のものを入力してください。)
https://github.com/adash333/elm-firebase-chat2

DEMOサイト
https://romantic-noyce-862b75.netlify.com/


ElmとFirebaseでチャットアプリ目次

開発環境

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
    },

新規Elmアプリの作成

C:/elm/ フォルダに、elm-bulma-hamburger/ フォルダを作成し、VisualStudioCodeで開き、Ctrl+@でコマンドプロンプトを開き、以下を入力します。
途中で何か聞かれたらEnterを押します。

elm/json, elm/random, alex-tan/elm-dialog パッケージもインストールしておきます。

elm init
elm install elm/json
elm install elm/random
elm install alex-tan/elm-dialog

参考: https://github.com/qnoyxu/chat-room/blob/master/elm.json

参考2: https://package.elm-lang.org/packages/alex-tan/elm-dialog/latest/

ElmでBulmaを使用するための準備

Browser.documentからBulmaを使用するための以下の2つのファイル(index.html, .gitignore, netlify.toml)をコピペします。

Firebaseで新規プロジェクトの作成

チャット内容を保存するサーバとして、Firebaseアプリを新規作成します。詳細については、こちらを参考にしていただけますと幸いです。

  1. https://console.firebase.google.com にGoogleアカウントでログイン
  2. 新規プロジェクト作成
  3. Project Overview > 「開始するにはアプリを追加してください」のすぐ上の、「</>」をクリック
  4. 「ウェブアプリに Firebase を追加」画面が出てくるので、中身をメモ帳などにコピーしておく(後で、index.htmlにペーストします)
  5. Authentication > ログイン方法 > Google をクリックして、Google認証を「有効にする」をONにして保存
  6. Authentication > ログイン方法 の画面の下の方へ行き、承認済みドメイン のところに localhostが入っていることを確認する。(本番環境にデプロイする場合は、そのドメインを追加する。)

Project Overview
+アプリを追加
Firebase database の 画面に従って入力していくと、
var firebaseConfig = {
の画面が出てくるので、
databaseURL: “https://<YOUR-APPLICATION>.firebaseio.com”,
のところのアドレスをコピーしておきます。

注:”https://<YOUR-APPLICATION>.firebaseio.com” のところは、それぞれの人によって変わります。

なお、セキュリティ的にかなり微妙ですが、今回は、Databaseのルールの設定を、以下のようにしておきます。

{
  "rules": {
    ".read": true,
    ".write": true
  }す
}

index.htmlの編集

写経元サイトの
src/index.html
src/index.js
をコピペします。
参考: http://i-doctor.sakura.ne.jp/font/?p=38262

  • firebaseを使用できるようにする
  • Elmへportなどで、Firebaseとの通信を行えるようにする

ただし、

const config = {databaseURL: "https://(ここにご自身のFirebaseのURLを記載してください).firebaseio.com"};

のところは、ご自身のFirebaseアプリのURLを記載してください。

src/Main.elmの新規作成

src/Main.elm を新規作成し、引き続き、写経元サイトの 
https://github.com/qnoyxu/chat-room/blob/master/src/Main.elm  からコピーしていきます。

次に、
Model.elm
をここにコピーします。 init の部分を統合しました。

次に、
Update.elm
をコピーします。UPDATEはそのまま、Main.elmにコピペしました。

次に、
View.elm と  Ports.elm
をここにコピーします。

このとき、一番上の行の
module Main exposing (main)
の前に、port を加えて、

port module Main exposing (main)

にしておきます。

SCSS, CSS関連のコードを消す

scssは苦手なので、SCSS, CSS関連のコードを手作業で消していきます。(大丈夫だろうか、、、)

あと、
map

List.map
に書き換えたり、

object  を Json.Encode.object に書き換えたりして、エラーを減らしていきました。

ここで一度、elm-liveで開発サーバで表示してみます。

elm-live src/Main.elm --open -- --output=main.js

CSSをすべて削除したので見栄えはむちゃくちゃですが、一応、Firebaseからデータは取ってこれているようです。

右上の方で、名前”NoCSSMan”を入力して OK を押し、左下の方でチャットコメントを入力して Send を押すと、たしかにチャットコメントも反映されました。

次は、見栄えをなんとかしてみたいです。

Bulmaで少し見栄えを変更する(1)

こちらのサイトを参考にして、少し見栄えを変更してみます。(参考2:Bulmaで画像

さらに、HTML to ELM を参考にして、書き換えてみます。

ヘッダーの部分を追加

nav [ class "navbar has-background-info is-fixed-top" ]
            [ div [ class "navbar-brand" ]
                [ a [ class "navbar-item has-text-white", href "#" ]
                    [ text "Elm Firebase Bulma Chat" ]
                ]
            ]

フッターの部分を編集

(変更前)

viewForm : Message -> Html Msg
viewForm message =
       Html.form [ onSubmit Submit ]
           [ textarea [ placeholder "Message", wrap "hard", value message, onInput ChangeMessage ] []
           , button
               [ class "btn btn-success"
               , disabled <| String.isEmpty <| String.trim message
               ]
               [ text "send" ]
           ]

(変更後)

viewForm : Message -> Html Msg
viewForm message =
    section [ class "section" ]
        [ div [ class "container" ]
            [ div [ class "field has-addons" ]
                [ div [ class "control is-expanded" ]
                    [ input [ class "input", placeholder "Message", type_ "text", value message, onInput ChangeMessage ]
                        []
                    ]
                , div [ class "control" ]
                    [ a [ class "button is-info", onClick Submit, disabled <| String.isEmpty <| String.trim message ]
                        [ text "Send" ]
                    ]
                ]
            ]
        ]
elm-live src/Main.elm --open -- --output=main.js

いらすとやから、画像を2つダウンロードして、img/ フォルダに保存します。

 https://codesandbox.io/s/divine-sky-z2tnu で見栄えを少し練習してから、HTML to Elmを用いて、VIEWを変更してみます。

index.html の</head>のすぐ上に以下を挿入します。

    <link
      rel="stylesheet"
      href="styles.css"
    />

styles.css の新規作成

.media-body {
  padding: 5px 10px;
  background-color: #98fb98;
  border-radius: 6px;
  font-size: 12px;
  color: #555;
  left: 10%;
  right: 10%;
}

VIEWを作るのが結構ストレスです。とりあえず、今のところ、全員くまさんのアイコンで、コメントの位置も右側としておきます。

一番下まで自動スクロール

よくわからないのですが、自動的に一番下まですくろーるしてくれなくなってしまっていますので、自動スクロールされるようにしたいと思います。

写経元のコードを見てみると、Browser.Domの、

  • getViewportOf 関数
  • setViewportOf 関数

について調べる必要がありそうです。

jumpToBottom "history"


jumpToBottom : String -> Cmd Msg
jumpToBottom id =
    Dom.getViewportOf id
        |> Task.andThen (\info -> Dom.setViewportOf id 0 info.scene.height)
        |> Task.attempt (\_ -> NoOp)

https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom

https://package.elm-lang.org/packages/elm/browser/latest/Browser-Dom#setViewportOf

https://qiita.com/uzimaru0000/items/e92e672ab06d77389641
@uzimaru0000
2018年12月19日に更新
任意のイベントデータを含んだメッセージを作る方法とBrowser.Domについて

https://qiita.com/hibohiboo/items/f40315c34d921fba82f0
@hibohiboo
2019年03月22日に更新
Elmでページ内ジャンプをしたメモ

いろいろ試してみましたが、どうしても自動スクロールできません。

https://qiita.com/hibohiboo/items/f40315c34d921fba82f0

の、

「setViewPortOf は、IDで指定した スクロール可能な 要素を任意の位置にスクロールするものであって、スクロール可能な要素に 含まれる要素 をIDで指定してその要素のところまでスクロールさせるものではないです。」

の文面はわかった(つもり)のですが、じゃあ、どうやってやればよいのか、どうしてもわかりません。。。

elm-lang/Navigationは、Elm0.18までのパッケージなのか、私のelm0.19の環境ではインストールできず、Navigation.load 関数を用いることはできませんでした。

挫折か?、、、

今のところのコード(一部)は以下のようにしているのですが、どうしても、自動スクロールできませんでした。。。

elm-live src/Main.elm --open -- --output=main.js

MAIN, SUBSCRIPTIONS, とPORT

UPDATE

VIEW

Browser.Navigationを利用する

なんか勘違いしていたみたいで、以下のimportはできました。

import Browser.Navigation as Nav

https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation

しかし、

jumpToBottom “bottom"

のところを、

Nav.load “bottom"

Nav.load “index.html#bottom"

にしても、両方とも一番下まで自動スクロールしてくれませんでした。がっくし。。。

linuss/smooth-scrollパッケージを試してみる

linuss/smooth-scrollパッケージを試してみます。

参考:https://package.elm-lang.org/packages/linuss/smooth-scroll/latest/

Ellieで試してみます。

参考2:https://ellie-app.com/5Q6J4RdVfkca1

これをもとに、できるでしょうか、、、

パッケージのインストール

elm install linuss/smooth-scroll
elm-live src/Main.elm --open -- --output=main.js
SmoothScroll と Task をimport
import SmoothScroll exposing (scrollTo)
import Task exposing (Task)
type Msg に SmoothScroll String と NoOpを追加
(今回はもともとNoOpがあったので、SmoothScroll String のみを追加しました。)
type Msg
    = NoOp
    | SmoothScroll String

update msg modelに以下を追加します。

update msg model =
    case msg of
        NoOp ->
            ( model, Cmd.none )

        SmoothScroll id ->
            ( model, Task.attempt (always NoOp) (scrollTo id) )

NewMessage value ->
case decodeMessage value of
Ok form ->
のところの、

jumpToBottom "bottom"

のところを、

Task.attempt (always NoOp) (scrollTo "bottom")

に変更します。

(VIEWのところで、一番下のコメント入力欄のところを div [ Html.Attributes.id “bottom" ] [ viewForm model.message ]  として、<div id="bottom">となるようにしています。)

(最初、 ここの部分に、SmoothScroll “bottom" と書いて、『型が違うよ!』と怒られてました。)

これでやっと、FirebaseからチャットのリストをとってきてDecodeが終了した時点で、一番下まで自動スクロールしてくれるようになりました。ふう。

今度、 時間のあるときに
https://github.com/linuss/smooth-scroll
のソースコードを読んでみたいです。

https://github.com/linuss/smooth-scroll/blob/master/src/SmoothScroll.elm
コードの一部は以下のようになっており、やはり、

Dom.setViewport
Dom.getViewport

を用いているようです。読んでもわかりませんが、、、

scrollTo : String -> Task Dom.Error (List ())
scrollTo =
    scrollToWithOptions defaultConfig

scrollToWithOptions : Config -> String -> Task Dom.Error (List ())
scrollToWithOptions config id =
    let
        tasks from to =
            List.map (Dom.setViewport 0)
                (animationSteps config.speed config.easing from (to - toFloat config.offset))
                |> Task.sequence
    in
    Task.map2 Tuple.pair Dom.getViewport (Dom.getElement id)
        |> Task.andThen (\( { viewport }, { element } ) -> tasks viewport.y element.y)

Bulmaで少し見栄えを変更する(2)

コメントリストの表示を、発言者が自分の場合と他人の場合で、絵を分けてみたいと思います。(他人は全員同じくまさんになってしまいますが、今回はこれ以上つっこまないことにします。)

(変更前)

[ p [ class "image is-64x64" ]
    [ img [ class "is-rounded", src "img/kuma.png" ] []
    ]
]

(変更後)

[ p [ class "image is-64x64" ]
    [ img
        [ class "is-rounded"
        , src
            (if messageClass == "self" then
                "img/usagi.png"

                else
                "img/kuma.png"
            )
        ]
        []
    ]
]

さらに、ぐちゃぐちゃやって、以下のようになりました。本当はCSSの float を使うべきだとは思うのですが、よくわからないので、ぐちゃぐちゃなコードですが、以下のような感じになりました。これ以上は突っ込まないことにします。

Ctrl+C => y + Enterで開発サーバを停止し、以下を入力します。

git add .
git commit -m "self image to usagi"
git push

Netlifyにデプロイ

ターミナル画面で以下を入力して、production用のmain.jsを作成します。(今回はUglify.jsは無視します。)

elm make src/Main.elm --output=main.js --optimize

以下を入力して、GitHub(またはBitBucket)にpushします。

git add .
git commit -m "elm make"
git push

https://www.netlify.com/ にログインして、上記GitHub(またはBitBucket)のリポジトリを選択して、デプロイします。設定欄は空白のままです。

デプロイできたようです。
https://romantic-noyce-862b75.netlify.com/

自分はウサギ、自分以外はみんなクマですが、まあ、気にしないことにします。

ソースコードとDEMOサイト

ソースコード
(index.htmlの55行目のFirebaseのアドレスはご自身のものを入力してください。)
https://github.com/adash333/elm-firebase-chat2

DEMOサイト
https://romantic-noyce-862b75.netlify.com/