今回は別ユーザーが投稿したメッセージをリアルタイムでチャット画面に表示していきましょう。
今回の実装は Cloud Run 上では多分うまく動かないので次回対応します。
方針
Socket.IOでは room という機能があって、roomに参加しているメンバーに対してだけメッセージを送信することができます。
なので、同じチャットを開いているユーザーがチャットに対応するroomに参加するような実装にすれば受け取ったメッセージを表示するだけでOKになります。
が、元のudemyの講座ではチャット画面を開いていないユーザーに、メッセージが来たことを知らせるために下記のような方針で実装しています。
- webアプリを開いた時点でユーザーIDをIDとするroomに参加する
- チャットで誰かが発言したら発言者以外のメンバーに対応するそれぞれのroomにメッセージを送る
- メッセージを受け取ったユーザーが該当のチャット画面を開いていればその画面に新しいメッセージを表示する
実装
client/src/App.vue
...
const store = useStore();
const socket = io();
socket.onAny((event, ...args) => {
store.dispatch("handleSocketEvent", { event, args });
});
store.dispatch("setSocket", { socket });
...
ルームへの参加はログイン後でなければならないので、ここでは接続だけしてイベントのハンドリングをstoreでできるようにしておきます。
client/src/store/index.js
...
async setSocket({ commit }, args) {
commit("setSocket", { socket: args.socket });
},
async handleSocketEvent({ commit }, args) {
switch (args.event) {
case "message":
commit("addMessage", { message: args.args[0] });
break;
}
},
async joinRoom({ state }, args) {
state.socket.emit("join", { _id: args.id });
},
...
この実装だと別のチャットを開いていてもメッセージが追加されてしまいますが、ざっくりとした動きをまず作ってしまいたいので放置します。
server/main.py
...
@socketio.on("join")
def join(payload):
if not payload.get("_id"):
return
join_room(payload.get("_id"))
server/apis/chats.py
...
@app.route("/api/chats/<chat_id>/messages", methods=["POST"])
@login_required()
def create_message(chat_id):
body = request.json
user_id = session["user"]["_id"]
error_message = Message.validate(body, user_id)
if error_message:
return error(error_message)
chat = Chat._collection.find_one({"_id": ObjectId(chat_id)})
if not chat or ObjectId(user_id) not in chat.get("users", []):
return error("チャットが存在しないか、参加していないチャットです")
message = Message(content=body["content"], chat=chat_id, sender=user_id)
message.create()
message = vars(message)
message["sender"] = session["user"]
message["_id"] = str(message["_id"])
message["chat"] = str(message["chat"])
message["created_at"] = str(message["created_at"])
message["updated_at"] = str(message["updated_at"])
for uid in chat.get("users"):
if str(uid) == user_id:
continue
socketio.emit("message", message, room=str(uid))
return jsonify(message)
...
自分以外にメッセージを送ります。
無理矢理感が結構強いものの、一応動くようにはなりました。
次回細かい部分を修正していきましょう。