Skip to content

Commit 6c27593

Browse files
committed
STEP7 チャットの実装
1 parent 80820b6 commit 6c27593

File tree

3 files changed

+71
-11
lines changed

3 files changed

+71
-11
lines changed

src/app/shared/firestore.service.ts

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,52 @@
11
import { Injectable } from '@angular/core';
2-
import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
3-
import { first } from 'rxjs/operators';
2+
import { AngularFirestore, AngularFirestoreDocument,
3+
AngularFirestoreCollection } from '@angular/fire/firestore';
4+
import { Observable } from 'rxjs';
5+
import { first, concatMap } from 'rxjs/operators';
46

57
export interface IUser {
68
displayName: string;
79
photoDataUrl: string;
810
}
911

12+
export interface IMessage {
13+
uid: string;
14+
message: string;
15+
timestamp: number;
16+
}
17+
18+
export interface IChat extends IUser, IMessage {}
19+
1020
@Injectable({
1121
providedIn: 'root'
1222
})
1323
export class FirestoreService {
1424
userDoc: AngularFirestoreDocument<IUser>;
15-
constructor(public af: AngularFirestore) { }
25+
messageCollection: AngularFirestoreCollection<IMessage>;
26+
userCollection: AngularFirestoreCollection<IUser>;
27+
28+
constructor(public af: AngularFirestore) {
29+
this.messageCollection = this.af.collection <
30+
IMessage
31+
>('chat', ref => ref.orderBy('timestamp', 'desc'));
32+
this.userCollection = this.af.collection<IUser>('users');
33+
}
34+
35+
messageAdd(message: IMessage) {
36+
return this.messageCollection.add(message);
37+
}
38+
39+
chatInit(): Observable<IChat[]> {
40+
return this.messageCollection.valueChanges( {idField: 'messageId'} )
41+
.pipe(concatMap(async message => {
42+
const users = await this.userCollection.valueChanges( {idField: 'uid'} )
43+
.pipe(first()).toPromise(Promise);
44+
return message.map(message => {
45+
const user = users.find(u => u.uid === message.uid);
46+
return Object.assign(message, user);
47+
});
48+
}));
49+
}
1650

1751
userInit(uid: string): Promise<IUser> {
1852
this.userDoc = this.af.doc<IUser>('users/' + uid);

src/app/tab1/tab1.page.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
チャット
55
</ion-title>
66
<ion-buttons slot="end">
7-
<ion-button fill="clear" size="small">送信</ion-button>
7+
<ion-button fill="clear" size="small" (click)="postMessage()" [disabled]="!f.form.valid">送信</ion-button>
88
</ion-buttons>
99
</ion-toolbar>
1010
<ion-toolbar color="primary" style="padding: 4px 8px">
1111
<ion-avatar slot="start" style="width: 36px; height: 36px;">
12-
<ion-img src="/assets/shapes.svg"></ion-img>
12+
<ion-img [src]="user?.photoDataUrl || '/assets/shapes.svg'"></ion-img>
1313
</ion-avatar>
1414
<form #f=ngForm>
1515
<ion-textarea class="ion-margin-start" autoGrow="true" rows="1"
@@ -20,15 +20,15 @@
2020

2121
<ion-content>
2222
<ion-list class="ion-padding-top">
23-
<ion-item lines="none" *ngFor="let m of [1,2,3,4,5]">
23+
<ion-item lines="none" *ngFor="let c of chat | async; trackBy: trackByFn">
2424
<ion-avatar slot="start">
25-
<ion-img src="/assets/shapes.svg" *ngIf="m !== 3"></ion-img>
25+
<ion-img [src]="c.photoDataUrl" *ngIf="c.uid !== uid"></ion-img>
2626
</ion-avatar>
2727
<ion-label class="ion-padding ion-text-wrap"
2828
style="background-color: var(--ion-color-light-tint); border-radius: 12px;"
29-
[class.ion-margin-end]="m !== 3" [class.ion-margin-start]="m === 3">
30-
<div class="ion-text-nowrap"><strong>表示名</strong></div>
31-
メッセージ
29+
[class.ion-margin-end]="c.uid !== uid" [class.ion-margin-start]="c.uid === uid">
30+
<div class="ion-text-nowrap"><strong>{{c.displayName}}</strong></div>
31+
{{c.message}}
3232
</ion-label>
3333
</ion-item>
3434
</ion-list>

src/app/tab1/tab1.page.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { Component, OnInit } from '@angular/core';
22
import { ModalController } from '@ionic/angular';
33
import { ProfilePage } from '../shared/profile/profile.page';
44
import { AuthService } from '../auth/auth.service';
5-
import { FirestoreService } from '../shared/firestore.service';
5+
import { FirestoreService, IChat, IUser } from '../shared/firestore.service';
6+
import { Observable } from 'rxjs';
67

78
@Component({
89
selector: 'app-tab1',
@@ -11,6 +12,9 @@ import { FirestoreService } from '../shared/firestore.service';
1112
})
1213
export class Tab1Page implements OnInit {
1314
message = '';
15+
uid: string;
16+
user: IUser;
17+
chat: Observable<IChat[]>;
1418
constructor(
1519
public modalController: ModalController,
1620
public auth: AuthService,
@@ -25,5 +29,27 @@ export class Tab1Page implements OnInit {
2529
});
2630
await modal.present();
2731
}
32+
this.chat = this.firestore.chatInit();
33+
}
34+
35+
async ionViewWillEnter() {
36+
this.uid = this.auth.getUserId();
37+
this.user = await this.firestore.userInit(this.uid);
38+
}
39+
40+
postMessage() {
41+
if (!this.user) {
42+
alert('プロフィール登録が必要です');
43+
return;
44+
}
45+
this.firestore.messageAdd({
46+
uid: this.uid,
47+
message: this.message,
48+
timestamp: Date.now(),
49+
});
50+
}
51+
52+
trackByFn(index, item) {
53+
return item.messageId;
2854
}
2955
}

0 commit comments

Comments
 (0)