AngularFire ウェブ Codelab

1. 概要

この Codelab では、AngularFire を使用して、Firebase のプロダクトとサービスを使用するチャット クライアントを実装およびデプロイすることで、ウェブ アプリケーションを作成する方法を学びます。

ユーザーが Firebase について話し合っているチャットアプリ

学習内容

  • Angular と Firebase を使用してウェブアプリを構築します。
  • Cloud Firestore と Cloud Storage for Firebase を使用してデータを同期する。
  • Firebase Authentication を使用してユーザーを認証する。
  • Firebase App Hosting にウェブアプリをデプロイする。
  • Firebase Cloud Messaging を使用して通知を送信する。
  • ウェブアプリのパフォーマンス データを収集する。

必要なもの

  • GitHub アカウント
  • Firebase プロジェクトを Blaze お支払いプランにアップグレードする機能
  • 任意の IDE またはテキスト エディタ(WebStormSublimeVS Code など)
  • パッケージ マネージャー npm(通常は Node.js に付属しています)
  • ターミナル/コンソール
  • 任意のブラウザ(Chrome など)
  • Codelab のサンプルコード(コードの入手方法については、Codelab の次のステップをご覧ください)。

2. サンプルコードを取得する

GitHub リポジトリを作成する

Codelab のソースは https://github.com/firebase/codelab-friendlychat-web にあります。このリポジトリには、複数のプラットフォーム用のサンプル プロジェクトが含まれています。ただし、この Codelab では angularfire-start ディレクトリのみを使用します。

angularfire-start フォルダを独自のリポジトリにコピーします。

  1. ターミナルを使用して、コンピューターに新しいフォルダを作成し、新しいディレクトリに移動します。
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. giget npm パッケージを使用して、angularfire-start フォルダのみを取得します。
    npx giget@latest gh:firebase/codelab-friendlychat-web/angularfire-start#master . --install
    
  3. git を使用してローカルで変更を追跡します。
    git init
    
    git add .
    
    git commit -m "codelab starting point"
    
    git branch -M main
    
  4. 新しい GitHub リポジトリ(https://github.com/new)を作成します。任意の名前を付けます。
    1. GitHub から、https://github.com/[user-name]/[repository-name].git または git@github.com:[user-name]/[repository-name].git のような新しいリポジトリ URL が提供されます。この URL をコピーします。
  5. ローカルの変更を新しい GitHub リポジトリに push します。your-repository-url プレースホルダにリポジトリ URL を挿入して、次のコマンドを実行します。
    git remote add origin your-repository-url
    
    git push -u origin main
    
  6. GitHub リポジトリにスターター コードが表示されます。

3. Firebase プロジェクトを作成して設定する

Firebase プロジェクトを作成する

  1. Firebase コンソールにログインします。
  2. Firebase コンソールで [プロジェクトを追加] をクリックし、Firebase プロジェクトに FriendlyChat という名前を付けます。Firebase プロジェクトのプロジェクト ID を覚えておいてください。
  3. [このプロジェクトで Google アナリティクスを有効にする] のチェックを外します。
  4. [プロジェクトを作成] をクリックします。

これから構築するアプリケーションでは、ウェブアプリで利用可能な Firebase プロダクトを使用します。

  • ユーザーがアプリに簡単にログインできるようにする Firebase Authentication
  • 構造化されたデータをクラウドに保存し、データが変更されたときに即座に通知を受け取る Cloud Firestore
  • ファイルをクラウドに保存する Cloud Storage for Firebase
  • Firebase App Hosting: アプリのビルド、ホスト、配信。
  • プッシュ通知を送信し、ブラウザのポップアップ通知を表示する Firebase Cloud Messaging
  • Firebase Performance Monitoring: アプリのユーザー パフォーマンス データを収集します。

この中には、特別な設定が必要になるプロダクトや、Firebase コンソールを使用して有効化する必要があるプロダクトがあります。

Firebase の料金プランをアップグレードする

Firebase App Hosting と Cloud Storage for Firebase を使用するには、Firebase プロジェクトが従量課金制(Blaze)のお支払いプランCloud 請求先アカウントにリンクされている)である必要があります。

  • Cloud 請求先アカウントには、クレジット カードなどのお支払い方法が必要です。
  • Firebase と Google Cloud を初めて使用する場合は、$300 のクレジットと無料トライアル用 Cloud 請求先アカウントを利用できるかどうか確認してください。
  • この Codelab をイベントの一環として実施する場合は、利用可能な Cloud クレジットがあるかどうかを主催者に確認してください。

プロジェクトを Blaze プランにアップグレードする手順は次のとおりです。

  1. Firebase コンソールで、プランをアップグレードを選択します。
  2. Blaze プランを選択します。画面上の手順に沿って、Cloud 請求先アカウントをプロジェクトにリンクします。
    このアップグレードの一環として Cloud 請求先アカウントを作成する必要があった場合は、Firebase コンソールのアップグレード フローに移動してアップグレードを完了する必要があります。

Firebase ウェブアプリをプロジェクトに追加する

  1. ウェブアイコン 58d6543a156e56f9.png をクリックして、新しい Firebase ウェブアプリを作成します。
  2. Friendly Chat というニックネームでアプリを登録します。[このアプリの Firebase Hosting も設定します] の横にあるチェックボックスをオンにせず、[アプリを登録] をクリックします。
  3. 次のステップでは、構成オブジェクトが表示されます。現時点では必要ありません。[コンソールに進む] をクリックします。

ウェブアプリの登録画面のスクリーンショット

Authentication を設定する

ユーザーが Google アカウントでウェブアプリにログインできるようにするには、Google ログイン方法を使用します。

  1. Firebase コンソールで [認証] に移動します。
  2. [Get started] をクリックします。
  3. [その他のプロバイダ] 列で、[Google] > [有効にする] をクリックします。
  4. [プロジェクトの公開名] テキスト ボックスに、覚えやすい名前(My Next.js app など)を入力します。
  5. [プロジェクトのサポートメール] プルダウンからメールアドレスを選択します。
  6. [保存] をクリックします。

Cloud Firestore を設定する

このウェブアプリは Cloud Firestore を使用してチャット メッセージを保存し、新しいチャット メッセージを受信します。

Firebase プロジェクトで Cloud Firestore を設定する方法は次のとおりです。

  1. Firebase コンソールの左側のパネルで [Build] を開き、[Firestore データベース] を選択します。
  2. [データベースを作成] をクリックします。
  3. [データベース ID] は (default) のままにします。
  4. データベースのロケーションを選択し、[次へ] をクリックします。
    実際のアプリの場合は、ユーザーに近いロケーションを選択します。
  5. [テストモードで開始] をクリックします。セキュリティ ルールに関する免責条項を確認します。
    この Codelab の後半で、セキュリティ ルールを追加してデータを保護します。データベースのセキュリティ ルールを追加せずに、アプリを配布または公開しないでください。
  6. [作成] をクリックします。

Cloud Storage for Firebase を設定する

このウェブアプリは Cloud Storage for Firebase を使用して画像ファイルを保存、アップロード、共有します。

Firebase プロジェクトで Cloud Storage for Firebase を設定する方法は次のとおりです。

  1. Firebase コンソールの左側のパネルで [Build] を開き、[Storage] を選択します。
  2. [開始] をクリックします。
  3. デフォルトの Storage バケットのロケーションを選択します。
    US-WEST1US-CENTRAL1US-EAST1 のバケットでは、Google Cloud Storage の「Always Free」階層を利用できます。他のすべてのロケーションのバケットは、Google Cloud Storage の料金と使用量に従います。
  4. [テストモードで開始] をクリックします。セキュリティ ルールに関する免責条項を確認します。
    この Codelab の後半で、セキュリティ ルールを追加してデータを保護します。Storage バケットのセキュリティ ルールを追加せずに、アプリを配布または公開しないでください。
  5. [作成] をクリックします。

4. Firebase コマンドライン インターフェースをインストールする

Firebase コマンドライン インターフェース(CLI)を使用すると、Firebase Hosting を使用してウェブアプリをローカルで提供したり、Firebase プロジェクトにウェブアプリをデプロイしたりできます。

  1. 次の npm コマンドを実行して、CLI をインストールします。
npm -g install firebase-tools@latest
  1. 次のコマンドを実行して、CLI が正しくインストールされたことを確認します。
firebase --version

Firebase CLI のバージョンが vv13.9.0 以降であることを確認します。

  1. 次のコマンドを実行して、Firebase CLI を承認します。
firebase login

ウェブアプリ テンプレートを設定して、アプリのローカル ディレクトリ(この Codelab でクローンを作成したリポジトリ)から Firebase Hosting 用のアプリの構成を pull するようにしました。ただし、構成を pull するには、アプリを Firebase プロジェクトに関連付ける必要があります。

  1. コマンドラインがアプリのローカル angularfire-start ディレクトリにアクセスしていることを確認します。
  2. 次のコマンドを実行して、アプリを Firebase プロジェクトに関連付けます。
firebase use --add
  1. プロンプトが表示されたら、プロジェクト ID を選択して、Firebase プロジェクトにエイリアスを指定します。

エイリアスは、複数の環境(本番環境、ステージング環境など)を使用する場合に役立ちます。ただし、この Codelab では、default というエイリアスのみを使用します。

  1. コマンドラインで残りの手順に沿って操作します。

5. AngularFire をインストールする

プロジェクトを実行する前に、Angular CLI と AngularFire が設定されていることを確認してください。

  1. コンソールで次のコマンドを実行します。
npm install -g @angular/cli
  1. 次に、コンソールで angularfire-start ディレクトリから次の Angular CLI コマンドを実行します。
ng add @angular/fire

これにより、プロジェクトに必要なすべての依存関係がインストールされます。

  1. プロンプトが表示されたら、Space キーを押して ng deploy -- hosting のチェックを外します。矢印キーとスペースバーを使用して、次の機能を選択します。
    • Authentication
    • Firestore
    • Cloud Messaging
    • Cloud Storage
  2. enter を押して、残りの手順に沿って操作します。
  3. commit メッセージ「Install AngularFire」で commit を作成し、GitHub リポジトリに push します。

6. App Hosting バックエンドを作成する

このセクションでは、Git リポジトリのブランチを監視する App Hosting バックエンドを設定します。

このセクションの終わりまでに、GitHub のリポジトリに接続された App Hosting バックエンドが作成されます。このバックエンドは、main ブランチに新しい commit を push するたびに、アプリの新しいバージョンを自動的に再ビルドしてロールアウトします。

  1. Firebase コンソールの [App Hosting] ページに移動します。

App Hosting コンソールのゼロ状態([使ってみる] ボタンあり)

  1. [使ってみる] をクリックして、バックエンドの作成フローを開始します。バックエンドを次のように構成します。
  2. 最初の手順のメッセージに沿って、先ほど作成した GitHub リポジトリを接続します。
  3. デプロイの設定を設定します。
    1. ルート ディレクトリを / のままにする
    2. ライブブランチを main に設定します。
    3. 自動ロールアウトを有効にする
  4. バックエンドに friendlychat-codelab という名前を付けます。
  5. [Firebase ウェブアプリを作成または関連付ける] で、[既存の Firebase ウェブアプリを選択する] プルダウンから、前に構成したウェブアプリを選択します。
  6. [完了してデプロイ] をクリックします。しばらくすると、新しいページが表示され、新しい App Hosting バックエンドのステータスを確認できます。
  7. ロールアウトが完了したら、[ドメイン] で無料ドメインをクリックします。DNS の伝播により、動作を開始するまでに数分かかることがあります。

これで、最初のウェブアプリがデプロイされました。GitHub リポジトリの main ブランチに新しい commit を push するたびに、Firebase コンソールで新しいビルドとロールアウトが開始されます。ロールアウトが完了すると、サイトが自動的に更新されます。

App Hosting コンソールのゼロ状態([使ってみる] ボタンあり)

FriendlyChat アプリのログイン画面が表示されます(まだ機能しません)。

現時点ではアプリは何もできませんが、皆様のご協力により、まもなく機能するようになります。

では、リアルタイム チャット アプリを作成しましょう。

7. Firebase をインポートして設定する

Firebase を設定する

使用している Firebase プロジェクトを Firebase SDK に通知するように Firebase SDK を構成する必要があります。

  1. Firebase コンソールのプロジェクト設定に移動します。
  2. [マイアプリ] カードで、構成オブジェクトが必要なアプリのニックネームを選択します。
  3. Firebase SDK スニペット ペインで [Config] を選択します。

環境ファイル /angularfire-start/src/environments/environment.ts が生成されていることがわかります。

  1. 構成オブジェクトのスニペットをコピーして、angularfire-start/src/firebase-config.js に追加します。

environment.ts

export const environment = {
  firebase: {
    apiKey: "API_KEY",
    authDomain: "PROJECT_ID.firebaseapp.com",
    projectId: "PROJECT_ID",
    storageBucket: "PROJECT_ID.firebasestorage.app",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID",
  },
};

AngularFire の設定を表示する

コンソールで選択した機能が /angularfire-start/src/app/app.config.ts ファイルに自動的に追加されていることがわかります。これにより、アプリで Firebase の機能と機能を使用できるようになります。

8. ユーザーのログインを設定する

AngularFire は app.config.ts でインポートされ、初期化されているため、使用できる状態になっています。次に、Firebase Authentication を使用してユーザー ログインを実装します。

承認済みドメインを追加します

Firebase Authentication では、管理者が設定したドメインのリストからのログインのみが許可されます。無料の App Hosting ドメインをドメインのリストに追加します。

  1. [App Hosting] に移動します。
  2. バックエンドのドメインをコピーします。
  3. [認証設定] に移動します。
  4. [承認済みドメイン] タブを選択します。
  5. [ドメインを追加] をクリックし、App Hosting バックエンドのドメインを貼り付けます。

Google ログインでユーザーを認証する

アプリでユーザーが [Google でログイン] ボタンをクリックすると、login 関数がトリガーされます。この Codelab では、Google を ID プロバイダとして使用することを Firebase に許可します。ここではポップアップを使用しますが、Firebase では他の方法も使用できます。

  1. サブディレクトリ /src/app/services/chat.service.ts を開きます。
  2. 関数 login を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Signs-in Friendly Chat.
login() {
    signInWithPopup(this.auth, this.provider).then((result) => {
        const credential = GoogleAuthProvider.credentialFromResult(result);
        this.router.navigate(['/', 'chat']);
        return credential;
    })
}

logout 関数は、ユーザーが [ログアウト] ボタンをクリックするとトリガーされます。

  1. ファイル src/app/services/chat.service.ts に戻ります。
  2. 関数 logout を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Logout of Friendly Chat.
logout() {
    signOut(this.auth).then(() => {
        this.router.navigate(['/', 'login'])
        console.log('signed out');
    }).catch((error) => {
        console.log('sign out error: ' + error);
    })
}

認証状態を追跡する

UI を適宜更新するには、ユーザーがログインしているか、ログアウトしているかを確認する方法が必要です。AngularFire には、認証状態が変化するたびに更新されるオブザーバブルを取得する関数が用意されています。これはすでに実装されていますが、確認する価値があります。

  1. ファイル src/app/services/chat.service.ts に戻ります。
  2. 変数の割り当て user$ を見つけます。

chat.service.ts

// observable that is updated when the auth state changes
user$ = user(this.auth);

上記のコードは、オブザーバブルのユーザーを返す AngularFire 関数 user を呼び出します。認証状態が変化するたびに(ユーザーがログインまたはログアウトするたびに)トリガーされます。FriendlyChat の Angular テンプレート コンポーネントは、このオブザーバブルを使用して、UI を更新してリダイレクトしたり、ヘッダー ナビゲーションにユーザーを表示したりします。

アプリへのログインをテストする

  1. commit メッセージ「Adding Google Authentication」で commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。
  3. ウェブアプリでページを更新し、ログインボタンと Google アカウントを使用してアプリにログインします。auth/operation-not-allowed というエラー メッセージが表示された場合は、Firebase コンソールで認証プロバイダとして Google ログインを有効にしたことを確認します。
  4. ログインすると、プロフィール写真とユーザー名が表示されます。angularfire-3.png

9. Cloud Firestore にメッセージを書き込む

このセクションでは、アプリの UI に入力されるデータを作成して、Cloud Firestore に書き込みます。これは Firebase コンソールで手動で実行できますが、アプリ自体で実行して、基本的な Cloud Firestore の書き込みを例示します。

データモデル

Cloud Firestore データは、コレクション、ドキュメント、フィールド、サブコレクションに分割されます。チャットの各メッセージは、messages というトップレベルのコレクションにドキュメントとして保存します。

688d7bc5fb662b57.png

Cloud Firestore にメッセージを追加する

ユーザーが書き込んだチャット メッセージを保存するには、Cloud Firestore を使用します。

このセクションでは、ユーザーがデータベースに新しいメッセージを書き込めるようにする機能を追加します。ユーザーが [送信] ボタンをクリックすると、以下のコード スニペットがトリガーされます。メッセージ フィールドの内容を含むメッセージ オブジェクトが、messages コレクション内の Cloud Firestore インスタンスに追加されます。add() メソッドは、自動生成された ID を持つ新しいドキュメントをコレクションに追加します。

  1. ファイル src/app/services/chat.service.ts に戻ります。
  2. 関数 addMessage を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Adds a text or image message to Cloud Firestore.
addMessage = async (
  textMessage: string | null,
  imageUrl: string | null,
): Promise<void | DocumentReference<DocumentData>> => {
  // ignore empty messages
  if (!textMessage && !imageUrl) {
    console.log(
      "addMessage was called without a message",
      textMessage,
      imageUrl,
    );
    return;
  }

  if (this.currentUser === null) {
    console.log("addMessage requires a signed-in user");
    return;
  }

  const message: ChatMessage = {
    name: this.currentUser.displayName,
    profilePicUrl: this.currentUser.photoURL,
    timestamp: serverTimestamp(),
    uid: this.currentUser?.uid,
  };

  textMessage && (message.text = textMessage);
  imageUrl && (message.imageUrl = imageUrl);

  try {
    const newMessageRef = await addDoc(
      collection(this.firestore, "messages"),
      message,
    );
    return newMessageRef;
  } catch (error) {
    console.error("Error writing new message to Firebase Database", error);
    return;
  }
};

メッセージの送信をテストする

  1. commit メッセージ「Post new chats to Firestore」で commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。
  3. FriendlyChat を更新します。ログイン後、「Hi there!」などのメッセージを入力し、[送信] をクリックします。これによりメッセージが Cloud Firestore に書き込まれます。ただし、データの「取得」を実装する必要があるため(Codelab の次のセクションで行います)、実際のウェブアプリにデータは表示されません
  4. Firebase コンソールで新しく追加されたメッセージを確認できます。Emulator Suite UI を開きます。[Build] セクションで [Firestore Database] をクリックします(またはこちらをクリックします)。新しく追加されたメッセージを含む messages コレクションが表示されます。

6812efe7da395692.png

10. メッセージを読む

メッセージを同期する

アプリでメッセージを読み取るには、データの変更時にトリガーされるオブザーバブルを追加し、新しいメッセージを表示する UI 要素を作成する必要があります。

アプリから新しく追加されたメッセージをリッスンするコードを追加します。このコードでは、messages コレクションのスナップショットを取得します。読み込み時に非常に長い履歴が表示されないように、チャットの最後の 12 件のメッセージのみが表示されます。

  1. ファイル src/app/services/chat.service.ts に戻ります。
  2. 関数 loadMessages を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Loads chat message history and listens for upcoming ones.
loadMessages = () => {
  // Create the query to load the last 12 messages and listen for new ones.
  const recentMessagesQuery = query(collection(this.firestore, 'messages'), orderBy('timestamp', 'desc'), limit(12));
  // Start listening to the query.
  return collectionData(recentMessagesQuery);
}

データベース内のメッセージをリッスンするには、collection 関数を使用してコレクションにクエリを作成し、リッスンするデータがどのコレクションにあるかを指定します。上記のコードでは、チャット メッセージが保存されている messages コレクション内の変更をリッスンします。また、limit(12) を使用して最後の 12 件のメッセージのみをリッスンするよう制限を適用し、orderBy('timestamp', 'desc') を使用して日付順にメッセージを並べ替え、最新の 12 件のメッセージを取得します。

collectionData 関数は内部でスナップショットを使用します。クエリに一致するドキュメントに変更があると、コールバック関数がトリガーされます。これは、メッセージが削除、変更、追加された場合のいずれかです。詳細については、Cloud Firestore のドキュメントをご覧ください。

メッセージの同期をテストする

  1. 「UI に新しいチャットを表示」という commit メッセージを含む commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。
  3. FriendlyChat を更新します。先ほどデータベースに作成したメッセージが FriendlyChat UI に表示されます(下記を参照)。新しいメッセージを書き込んでも、すぐに表示されるはずです。
  4. (省略可)エミュレータ スイートの [Firestore] セクションで、メッセージを手動で削除、変更したり、新しいメッセージを追加したりしてみてください。これらの変更も UI に反映されるはずです。

これで完了です。アプリで Cloud Firestore ドキュメントを読み取っています。

angularfire-2.png

11. AI 機能を追加する

Google AI を使用して、便利なユーザー補助機能をチャットアプリに追加します。

Google AI API キーを取得する

  1. Google AI Studio に移動し、[API キーを作成] をクリックします。
  2. この Codelab 用に作成した Firebase プロジェクトを選択します。プロンプトは Google Cloud プロジェクト用ですが、すべての Firebase プロジェクトは Google Cloud プロジェクトです。
  3. [Create API key in existing project](既存のプロジェクトで API キーを作成する)をクリックします。
  4. 生成された API キーをコピーします。

拡張機能をインストールする

この拡張機能は、Firestore の messages コレクションに新しいドキュメントが追加されるたびにトリガーされる Cloud Functions の関数をデプロイします。この関数は Gemini を呼び出し、ドキュメントの response フィールドにレスポンスを書き戻します。

  1. Build Chatbot with the Gemini API 拡張機能ページで、[Firebase コンソールでインストール] をクリックします。
  2. 画面の指示に従います。[拡張機能を構成する] の手順に進み、次のパラメータ値を設定します。
    • Gemini API プロバイダ: Google AI
    • Google AI API キー: 前に作成したキーを貼り付け、[シークレットを作成] をクリックします。
    • Firestore コレクションのパス: messages
    • プロンプト フィールド: text
    • レスポンス フィールド: response
    • 注文フィールド: timestamp
    • コンテキスト: Keep your answers short, informal, and helpful. Use emojis when possible.
  3. [拡張機能をインストール] をクリックします。
  4. 拡張機能のインストールが完了するまで待ちます。

AI 機能をテストする

FriendlyChat には、AI 拡張機能からレスポンスを読み取るコードがすでにあります。新しいチャット メッセージを送信するだけで、この機能をお試しいただけます。

  1. FriendlyChat を開いてメッセージを送信します。
  2. しばらくすると、メッセージの横に回答がポップアップ表示されます。生成 AI によって作成されたものであり、実際のユーザーによるものではないことを明確にするために、末尾に ✨ ai generated という注記が付いています。

12. 画像を送信する

次に、画像を共有する機能を追加します。

Cloud Firestore は構造化データを保存するのに適していますが、ファイルの保存には Cloud Storage が適しています。Cloud Storage for Firebase はファイル/blob ストレージ サービスです。このサービスを使用して、ユーザーがアプリで共有する画像を保存します。

画像を Cloud Storage に保存する

この Codelab では、ファイル選択ツール ダイアログをトリガーするボタンがすでに追加されています。ファイルを選択すると、saveImageMessage 関数が呼び出され、選択したファイルへの参照を取得できます。saveImageMessage 関数は次の処理を行います。

  1. チャット フィードに「プレースホルダ」チャット メッセージを作成して、画像のアップロード中に「読み込み中」のアニメーションがユーザーに表示されるようにします。
  2. 画像ファイルを Cloud Storage の /<uid>/<file_name> パスにアップロードします。
  3. 画像ファイルの一般公開 URL を生成します。
  4. 一時的に読み込まれる画像の代わりに、新しくアップロードされた画像ファイルの URL でチャット メッセージを更新します。

次に、画像を送信する機能を追加します。

  1. ファイル src/chat.service.ts に戻ります。
  2. 関数 saveImageMessage を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Saves a new message containing an image in Firestore.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
  try {
    // 1 - Add a message with a loading icon that will get updated with the shared image.
    const messageRef = await this.addMessage(null, this.LOADING_IMAGE_URL);

    // 2 - Upload the image to Cloud Storage.
    const filePath = `${this.auth.currentUser?.uid}/${file.name}`;
    const newImageRef = ref(this.storage, filePath);
    const fileSnapshot = await uploadBytesResumable(newImageRef, file);

    // 3 - Generate a public URL for the file.
    const publicImageUrl = await getDownloadURL(newImageRef);

    // 4 - Update the chat message placeholder with the image's URL.
    messageRef ?
    await updateDoc(messageRef, {
      imageUrl: publicImageUrl,
      storageUri: fileSnapshot.metadata.fullPath
    }): null;
  } catch (error) {
    console.error('There was an error uploading a file to Cloud Storage:', error);
  }
}

画像の送信をテストする

  1. commit メッセージ「Add the ability to post images」で commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。
  3. FriendlyChat を更新します。ログイン後、左下の画像アップロード ボタン angularfire-4.png をクリックし、ファイル選択ツールを使用して画像ファイルを選択します。画像をお探しの場合は、こちらのコーヒーカップの写真を自由にご利用ください。
  4. 選択した画像 angularfire-2.png が新しいメッセージとしてアプリの UI に表示されます。

ログインしていないときに画像を追加しようとすると、画像を追加するにはログインする必要があることを通知するエラーが表示されます。

13. 通知を表示する

ブラウザ通知のサポートを追加します。チャットに新しいメッセージが投稿されると、アプリからユーザーに通知が届きます。Firebase Cloud Messaging(FCM)は、メッセージや通知を無料で確実に配信するためのクロス プラットフォーム メッセージング ソリューションです。

FCM Service Worker を追加する

ウェブアプリには、ウェブ通知を受信して表示する Service Worker が必要です。

メッセージ プロバイダは AngularFire が追加されたときにすでに設定されているはずです。/angularfire-start/src/app/app.config.ts の imports セクションに次のコードが存在することを確認します。

provideMessaging(() => {
    return getMessaging();
}),

app/app.config.ts

Service Worker が Firebase Cloud Messaging SDK を読み込んで初期化することで、通知が表示されるようになります。

FCM デバイス トークンを取得する

デバイスまたはブラウザで通知を有効にすると、デバイス トークンが付与されます。このデバイス トークンは、特定のデバイスまたは特定のブラウザに通知を送信するために使用します。

ユーザーがログインすると、saveMessagingDeviceToken 関数を呼び出します。ここでブラウザから FCM デバイス トークンを取得し、Cloud Firestore に保存します。

chat.service.ts

  1. 関数 saveMessagingDeviceToken を見つけます。
  2. 関数全体を次のコードに置き換えます。

chat.service.ts

// Saves the messaging device token to Cloud Firestore.
saveMessagingDeviceToken= async () => {
    try {
      const currentToken = await getToken(this.messaging);
      if (currentToken) {
        console.log('Got FCM device token:', currentToken);
        // Saving the Device Token to Cloud Firestore.
        const tokenRef = doc(this.firestore, 'fcmTokens', currentToken);
        await setDoc(tokenRef, { uid: this.auth.currentUser?.uid });
 
        // This will fire when a message is received while the app is in the foreground.
        // When the app is in the background, firebase-messaging-sw.js will receive the message instead.
        onMessage(this.messaging, (message) => {
          console.log(
            'New foreground notification from Firebase Messaging!',
            message.notification
          );
        });
      } else {
        // Need to request permissions to show notifications.
        this.requestNotificationsPermissions();
      }
    } catch(error) {
      console.error('Unable to get messaging token.', error);
    };
}

ただし、このコードは最初は機能しません。アプリがデバイス トークンを取得できるようにするには、ユーザーがアプリに通知を表示する権限を付与する必要があります(Codelab の次のステップ)。

通知を表示する権限をリクエストする

ユーザーがアプリに通知を表示する権限をまだ付与していない場合、デバイス トークンは取得されません。この場合は、requestPermission() メソッドを呼び出します。これにより、この権限を求めるブラウザ ダイアログが表示されます(サポートされているブラウザ)。

8b9d0c66dc36153d.png

  1. ファイル src/app/services/chat.service.ts に戻ります。
  2. 関数 requestNotificationsPermissions を見つけます。
  3. 関数全体を次のコードに置き換えます。

chat.service.ts

// Requests permissions to show notifications.
requestNotificationsPermissions = async () => {
    console.log('Requesting notifications permission...');
    const permission = await Notification.requestPermission();
    
    if (permission === 'granted') {
      console.log('Notification permission granted.');
      // Notification permission granted.
      await this.saveMessagingDeviceToken();
    } else {
      console.log('Unable to get permission to notify.');
    }
}

デバイス トークンを取得する

  1. commit メッセージ「Add the ability to post images」で commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。
  3. FriendlyChat を更新します。ログインすると、通知に関する権限を求めるダイアログが表示されます。bd3454e6dbfb6723.png
  4. [許可] をクリックします。
  5. ブラウザの JavaScript コンソールを開きます。次のメッセージが表示されます。Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu
  6. デバイス トークンをコピーします。これは Codelab の次のステージで必要になります。

デバイスに通知を送信する

デバイス トークンが取得できたので、通知を送信できます。

  1. Firebase コンソールの [Cloud Messaging] タブを開きます。
  2. [New Notification] をクリックします。
  3. 通知のタイトルと通知テキストを入力します。
  4. 画面の右側にある [テスト メッセージを送信] をクリックします。
  5. ブラウザの JavaScript コンソールからコピーしたデバイス トークンを入力し、プラス記号(+)をクリックします。
  6. [test] をクリックします。

アプリがフォアグラウンドにある場合は、JavaScript コンソールに通知が表示されます。

アプリがバックグラウンドで動作している場合は、次の例のようにブラウザに通知が表示されます。

de79e8638a45864c.png

14. Cloud Firestore セキュリティ ルール

データベース セキュリティ ルールを表示する

Cloud Firestore は、特定のルール言語を使用して、アクセス権、セキュリティ、およびデータの妥当性を定義します。

この Codelab の開始時に Firebase プロジェクトを設定したときに、「テストモード」のデフォルトのセキュリティ ルールを使用することを選択し、データストアへのアクセスを制限しませんでした。Firebase コンソールの [データベース] セクションの [ルール] タブで、これらのルールを確認および変更できます。

デフォルトのルールが表示されます。このルールでは、データストアへのアクセスは制限されません。つまり、すべてのユーザーがデータストア内の任意のコレクションに対して読み取りと書き込みを行うことができます。

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

次のルールを使用して、制限するルールを更新します。

firestore.rules

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

セキュリティ ルールは、Emulator Suite に自動的に更新されます。

Cloud Storage セキュリティ ルールを表示する

Cloud Storage for Firebase は、特定のルール言語を使用して、アクセス権、セキュリティ、データの妥当性を定義します。

この Codelab の開始時に Firebase プロジェクトを設定したときに、認証されたユーザーのみに Cloud Storage の使用を許可するデフォルトの Cloud Storage セキュリティ ルールを使用することを選択しました。Firebase コンソールの [Storage] セクションの [Rules] タブで、ルールを確認および変更できます。ログインしたすべてのユーザーがストレージ バケット内のファイルを読み取り、書き込みできるようにするデフォルトのルールが表示されます。

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

ルールは次の目的で更新します。

  • 各ユーザーが自分の特定のフォルダにのみ書き込むことを許可する
  • Cloud Storage からの読み取りを誰にでも許可する
  • アップロードしたファイルが画像であることを確認する
  • アップロードできる画像のサイズを最大 5MB に制限する

これは、次のルールを使用して実装できます。

storage.rules

rules_version = '2';

// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

15. 完了

Firebase を使用してリアルタイム チャット ウェブ アプリケーションを作成しました。

学習した内容

  • Firebase App Hosting
  • Firebase Authentication
  • Cloud Firestore
  • Firebase SDK for Cloud Storage
  • Firebase Cloud Messaging
  • Firebase Performance Monitoring

次のステップ

詳細

16. [省略可] App Check で適用する

Firebase App Check は、不要なトラフィックからサービスを保護し、バックエンドを不正使用から保護します。このステップでは、認証情報の検証を追加し、App Check と reCAPTCHA Enterprise を使用して不正なクライアントをブロックします。

まず、App Check と reCaptcha を有効にする必要があります。

reCaptcha Enterprise の有効化

  1. Cloud コンソールで、[セキュリティ] で [reCaptcha Enterprise] を見つけて選択します。
  2. プロンプトが表示されたらサービスを有効にして、[キーを作成] をクリックします。
  3. メッセージに沿って表示名を入力し、プラットフォームの種類として [ウェブサイト] を選択します。
  4. デプロイした URL を [ドメインリスト] に追加し、[チェックボックスによる本人確認を使用する] オプションが選択されていないことを確認します。
  5. [鍵を作成] をクリックし、生成された鍵を安全な場所に保存します。このステップの後半で必要になります。

App Check を有効にする

  1. Firebase コンソールの左側のパネルで、[Build] セクションを見つけます。
  2. [App Check] をクリックし、[ログイン方法] タブをクリックして [App Check] に移動します。
  3. [登録] をクリックし、プロンプトが表示されたら reCaptcha Enterprise キーを入力して、[保存] をクリックします。
  4. [API] ビューで [ストレージ] を選択し、[適用] をクリックします。Cloud Firestore についても同様にします。

これで App Check が適用されます。アプリを更新して、チャット メッセージを表示または送信してみてください。次のエラー メッセージが表示されます。

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

つまり、App Check はデフォルトで未検証のリクエストをブロックします。次に、アプリに検証を追加します。

environment.ts ファイルに移動し、reCAPTCHAEnterpriseKeyenvironment オブジェクトに追加します。

export const environment = {
  firebase: {
    apiKey: 'API_KEY',
    authDomain: 'PROJECT_ID.firebaseapp.com',
    databaseURL: 'https://PROJECT_ID.firebaseio.com',
    projectId: 'PROJECT_ID',
    storageBucket: 'PROJECT_ID.firebasestorage.app',
    messagingSenderId: 'SENDER_ID',
    appId: 'APP_ID',
    measurementId: 'G-MEASUREMENT_ID',
  },
  reCAPTCHAEnterpriseKey: {
    key: "Replace with your recaptcha enterprise site key"
  },
};

key の値は、reCaptcha Enterprise トークンに置き換えます。

次に、app.config.ts ファイルに移動して、次のインポートを追加します。

import { getApp } from '@angular/fire/app';
import {
  ReCaptchaEnterpriseProvider,
  initializeAppCheck,
  provideAppCheck,
} from '@angular/fire/app-check';

同じ app.config.ts ファイルに、次のグローバル変数の宣言を追加します。

declare global {
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}

@NgModule({ ...

インポートで、ReCaptchaEnterpriseProvider を使用して App Check の初期化を追加し、isTokenAutoRefreshEnabledtrue に設定して、トークンの自動更新を許可します。

imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAppCheck(() => {
const appCheck = initializeAppCheck(getApp(), {
  provider: new ReCaptchaEnterpriseProvider(
  environment.reCAPTCHAEnterpriseKey.key
  ),
  isTokenAutoRefreshEnabled: true,
  });
  if (location.hostname === 'localhost') {
    self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
  }
  return appCheck;
}),

ローカルテストを許可するには、self.FIREBASE_APPCHECK_DEBUG_TOKENtrue に設定します。localhost でアプリを更新すると、次のようなデバッグ トークンがコンソールに記録されます。

App Check debug token: CEFC0C76-7891-494B-B764-349BDFD00D00. You will need to add it to your app's App Check settings in the Firebase console for it to work.

Firebase コンソールで App Check の [アプリビュー] に移動します。

オーバーフロー メニューをクリックし、[デバッグ トークンを管理] を選択します。

次に、[Add debug token] をクリックし、プロンプトが表示されたらコンソールからデバッグ トークンを貼り付けます。

chat.service.ts ファイルに移動し、次のインポートを追加します。

import { AppCheck } from '@angular/fire/app-check';

同じ chat.service.ts ファイルで、他の Firebase サービスとともに App Check を挿入します。

export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...
  1. 「App Check で未承認のクライアントをブロック」という commit メッセージで commit を作成し、GitHub リポジトリに push します。
  2. Firebase コンソールで [App Hosting] ページを開き、新しいロールアウトが完了するまで待ちます。

これで完了です。これで、アプリで App Check が機能するようになります。