Skip to content

Instantly share code, notes, and snippets.

@nishio

nishio/a.md Secret

Last active April 14, 2023 06:06
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nishio/cf5ed778b5703f3edfb1804081ec320e to your computer and use it in GitHub Desktop.
Save nishio/cf5ed778b5703f3edfb1804081ec320e to your computer and use it in GitHub Desktop.

0: user

チャットログをFirebaseに保存して、タブを閉じて開いた時にチャットログを復元するコードを書いて。言語はTypeScript

1: assistant

まず、Firebaseプロジェクトをセットアップし、必要な依存関係をインストールしてください。次に、以下のTypeScriptコードを使用して、チャットログをFirebaseに保存し、タブを閉じて開いたときにチャットログを復元できます。

  1. Firebaseの設定:
import firebase from "firebase/app";
import "firebase/firestore";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID",
};

firebase.initializeApp(firebaseConfig);

const db = firebase.firestore();
  1. チャットログをFirebaseに保存する関数:
interface ChatMessage {
  sender: string;
  message: string;
  timestamp: firebase.firestore.Timestamp;
}

async function saveChatMessage(chatMessage: ChatMessage) {
  try {
    await db.collection("chatlogs").add(chatMessage);
  } catch (error) {
    console.error("Error saving chat message: ", error);
  }
}
  1. タブを閉じて開いたときにチャットログを復元する関数:
async function restoreChatLog(): Promise<ChatMessage[]> {
  try {
    const chatLog: ChatMessage[] = [];
    const querySnapshot = await db.collection("chatlogs").orderBy("timestamp").get();

    querySnapshot.forEach((doc) => {
      const data = doc.data() as ChatMessage;
      chatLog.push(data);
    });

    return chatLog;
  } catch (error) {
    console.error("Error restoring chat log: ", error);
    return [];
  }
}

これで、saveChatMessage関数を使用してチャットメッセージをFirebaseに保存し、restoreChatLog関数を使用してタブを閉じて開いたときにチャットログを復元できます。ただし、これらの関数を実際のチャットアプリケーションに統合するには、適切なイベントリスナーやUIコンポーネントとともに使用する必要があります。

2: user

ユーザごとに分ける必要がある

3: assistant

ユーザごとにチャットログを分けるには、以下のようにコードを変更します。

  1. ユーザIDを含むチャットメッセージインターフェースを更新します。
interface ChatMessage {
  userId: string;
  sender: string;
  message: string;
  timestamp: firebase.firestore.Timestamp;
}
  1. ユーザIDを引数として受け取るように、saveChatMessage関数を変更します。
async function saveChatMessage(userId: string, chatMessage: ChatMessage) {
  try {
    await db.collection("chatlogs").doc(userId).collection("messages").add(chatMessage);
  } catch (error) {
    console.error("Error saving chat message: ", error);
  }
}
  1. ユーザIDを引数として受け取るように、restoreChatLog関数を変更します。
async function restoreChatLog(userId: string): Promise<ChatMessage[]> {
  try {
    const chatLog: ChatMessage[] = [];
    const querySnapshot = await db
      .collection("chatlogs")
      .doc(userId)
      .collection("messages")
      .orderBy("timestamp")
      .get();

    querySnapshot.forEach((doc) => {
      const data = doc.data() as ChatMessage;
      chatLog.push(data);
    });

    return chatLog;
  } catch (error) {
    console.error("Error restoring chat log: ", error);
    return [];
  }
}

これで、ユーザごとにチャットログが分かれるようになりました。saveChatMessagerestoreChatLog関数を呼び出すときに、ユーザIDを渡すことを忘れないでください。ユーザIDは、Firebase Authenticationなどの認証システムから取得できます。

4: user

UserIdをFirebase AuthのAnonymousで作って

5: assistant

Firebase Authenticationの匿名認証を使用してユーザーIDを作成するには、以下の手順に従ってください。

  1. まず、Firebaseプロジェクトで匿名認証を有効にします。Firebaseコンソールに移動し、Authenticationセクションで「サインイン方法」タブを選択し、匿名認証を有効にします。

  2. 次に、Firebase Authenticationをインポートし、匿名ユーザーとしてサインインする関数を作成します。

import "firebase/auth";

const auth = firebase.auth();

async function signInAnonymously(): Promise<string> {
  try {
    const result = await auth.signInAnonymously();
    return result.user?.uid || "";
  } catch (error) {
    console.error("Error signing in anonymously: ", error);
    return "";
  }
}
  1. この関数を呼び出して、匿名ユーザーとしてサインインし、ユーザーIDを取得します。
async function main() {
  const userId = await signInAnonymously();
  if (userId) {
    console.log("Signed in as anonymous user with ID: ", userId);
  } else {
    console.error("Failed to sign in anonymously");
  }
}

main();

これで、signInAnonymously関数を使用して匿名ユーザーとしてサインインし、ユーザーIDを取得できます。このユーザーIDをsaveChatMessageおよびrestoreChatLog関数に渡して、ユーザーごとにチャットログを保存および復元できます。

6: user

UIがAstroで書かれてるんだけど、どこに追加したらいい?

7: assistant

Astroを使用している場合、以下の手順でUIにチャット機能を追加できます。

  1. まず、src/componentsディレクトリに新しいAstroコンポーネントを作成します。例えば、Chat.astroという名前でファイルを作成しましょう。

  2. 次に、Chat.astroファイルに以下のコードを追加して、チャットUIの基本構造を作成します。

---
import { createRef } from 'astro';
const messagesRef = createRef();
const inputRef = createRef();
---
<html>
  <head>
    <style>
      /* ここにスタイルを追加 */
    </style>
  </head>
  <body>
    <div>
      <div ref={messagesRef}>
        <!-- ここにチャットメッセージが表示されます -->
      </div>
      <form>
        <input ref={inputRef} type="text" placeholder="メッセージを入力" />
        <button type="submit">送信</button>
      </form>
    </div>
  </body>
</html>
  1. Chat.astroファイルに、Firebaseとチャット関連の関数をインポートします。
---
import firebase from 'firebase/app';
import 'firebase/auth';
import 'firebase/firestore';
import { saveChatMessage, restoreChatLog, signInAnonymously } from '../lib/chat';
// ここでFirebaseの設定をインポートまたは定義します
---
  1. ページが読み込まれたときに、匿名ユーザーとしてサインインし、チャットログを復元するためのsetupChat関数を作成します。
---
async function setupChat() {
  const userId = await signInAnonymously();
  if (userId) {
    const chatLog = await restoreChatLog(userId);
    chatLog.forEach((message) => {
      addMessageToUI(message);
    });
  } else {
    console.error("Failed to sign in anonymously");
  }
}
---
  1. メッセージをUIに追加するaddMessageToUI関数を作成します。
---
function addMessageToUI(message: ChatMessage) {
  const messageElement = document.createElement('div');
  messageElement.textContent = `${message.sender}: ${message.message}`;
  messagesRef.current?.appendChild(messageElement);
}
---
  1. メッセージ送信時に、Firebaseにメッセージを保存するイベントリスナーを追加します。
---
function onFormSubmit(event: Event) {
  event.preventDefault();
  const messageText = inputRef.current?.value;
  if (messageText) {
    const chatMessage: ChatMessage = {
      userId: 'your-user-id', // 匿名ユーザーIDを使用する
      sender: 'your-sender-name', // 送信者名を設定する
      message: messageText,
      timestamp: firebase.firestore.Timestamp.now(),
    };
    saveChatMessage(chatMessage.userId, chatMessage);
    addMessageToUI(chatMessage);
    inputRef.current.value = '';
  }
}
---
  1. <form>要素にsubmitイベントリスナーを追加します。
<form on:submit={onFormSubmit}>
  1. 最後に、ページが読み込まれたときにsetupChat関数を呼び出します。
<body onload={setupChat}>

これで、Astroプロジェクトにチャット機能が追加されました。Chat.astroコンポーネントを、チャット機能を表示したいページにインポートして使用してください。

8: user

import { saveChatMessage, restoreChatLog, signInAnonymously } from '../lib/chat'; このファイルはどこに作る?TypeScript

9: assistant

../lib/chat は、プロジェクトのルートディレクトリに lib という名前のフォルダを作成し、その中に chat.ts という名前のTypeScriptファイルを作成することを意味しています。このファイルには、以前に説明した saveChatMessagerestoreChatLog、および signInAnonymously 関数を含めます。

プロジェクトのディレクトリ構造は次のようになります。

my-astro-project/
├─ src/
│  ├─ components/
│  │  ├─ Chat.astro
│  ├─ lib/
│  │  ├─ chat.ts

chat.ts ファイルには、以下のコードを含めます。

import firebase from "firebase/app";
import "firebase/auth";
import "firebase/firestore";

// ここでFirebaseの設定をインポートまたは定義します

firebase.initializeApp(firebaseConfig);

const auth = firebase.auth();
const db = firebase.firestore();

interface ChatMessage {
  userId: string;
  sender: string;
  message: string;
  timestamp: firebase.firestore.Timestamp;
}

export async function saveChatMessage(userId: string, chatMessage: ChatMessage) {
  try {
    await db.collection("chatlogs").doc(userId).collection("messages").add(chatMessage);
  } catch (error) {
    console.error("Error saving chat message: ", error);
  }
}

export async function restoreChatLog(userId: string): Promise<ChatMessage[]> {
  try {
    const chatLog: ChatMessage[] = [];
    const querySnapshot = await db
      .collection("chatlogs")
      .doc(userId)
      .collection("messages")
      .orderBy("timestamp")
      .get();

    querySnapshot.forEach((doc) => {
      const data = doc.data() as ChatMessage;
      chatLog.push(data);
    });

    return chatLog;
  } catch (error) {
    console.error("Error restoring chat log: ", error);
    return [];
  }
}

export async function signInAnonymously(): Promise<string> {
  try {
    const result = await auth.signInAnonymously();
    return result.user?.uid || "";
  } catch (error) {
    console.error("Error signing in anonymously: ", error);
    return "";
  }
}

これで、Chat.astro コンポーネントから chat.ts ファイルの関数をインポートして使用できます。

10: user

Astro+TypeScriptでESLintがParsing Errorする

11: assistant

Astroファイル内のTypeScriptコードをESLintで解析するには、いくつかの設定が必要です。以下の手順に従って、ESLintを設定してください。

  1. 必要なパッケージをインストールします。
npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-plugin-astro
  1. プロジェクトのルートディレクトリに .eslintrc.js ファイルを作成し、以下の内容を追加します。
module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: "module",
  },
  plugins: ["@typescript-eslint", "astro"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:astro/recommended",
  ],
  rules: {
    // ここにカスタムルールを追加できます
  },
};
  1. プロジェクトのルートディレクトリに .eslintignore ファイルを作成し、Astroファイル内のHTML部分を無視するように設定します。
*.astro:html

これで、Astroファイル内のTypeScriptコードをESLintで解析できるようになります。ESLintを実行するには、以下のコマンドを使用します。

npx eslint --ext .astro,.ts,.tsx src/

このコマンドは、src/ ディレクトリ内の .astro.ts、および .tsx ファイルを対象にESLintを実行します。必要に応じて、対象ディレクトリや拡張子を変更してください。

12: user

.eslintrc.jsで'module' is not defined.

13: assistant

Astroファイル内の<script>タグで import 文を使用する場合、ESLintが module が定義されていないというエラーを報告することがあります。これを解決するには、.eslintrc.jsファイルのenvプロパティにes2020を追加してください。

.eslintrc.jsファイルを以下のように更新します。

module.exports = {
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: "module",
  },
  plugins: ["@typescript-eslint", "astro"],
  extends: [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:astro/recommended",
  ],
  env: {
    es2020: true, // これを追加
  },
  rules: {
    // ここにカスタムルールを追加できます
  },
};

これで、module が定義されていないというエラーが解決されるはずです。

(追記: es2020: true ではなく node: true が正しいみたい)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment