Skip to content

Commit

Permalink
Load Chats
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela committed Jan 14, 2019
1 parent 894bcba commit c84fb58
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 8 deletions.
13 changes: 10 additions & 3 deletions client/src/app/graphql/graphql-root.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
import { pluck } from 'rxjs/operators';

import { Chat, Pages, PageChangeEvent, MessageEvent, ID } from '../whatsapp';
import GetChats from './queries/get-chats.graphql';

@Component({
selector: 'app-graphql-root',
Expand All @@ -15,15 +16,17 @@ import { Chat, Pages, PageChangeEvent, MessageEvent, ID } from '../whatsapp';
(message)="onMessage($event)"
(star)="toggleStar($event)"
(page)="onPage($event)"
></whatsapp>
>
<select-tool-button tool="loona"></select-tool-button>
</whatsapp>
`,
})
export class GraphQLRootComponent {
page: Pages = 'chats';
chats: Observable<Chat[]>;
chat: Observable<Chat>;

constructor() {}
constructor(private loona: Loona) {}

onPage(event: PageChangeEvent) {
this.page = event.page;
Expand All @@ -43,7 +46,11 @@ export class GraphQLRootComponent {

toggleStar(chatId: ID) {}

loadChats() {}
loadChats() {
this.chats = this.loona
.query(GetChats)
.valueChanges.pipe(pluck('data', 'chats'));
}

loadChat(id: ID) {}
}
2 changes: 2 additions & 0 deletions client/src/app/graphql/graphql.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { RestLink } from 'apollo-link-rest';

import { GraphQLRootComponent } from './graphql-root.component';
import { WhatsappModule } from '../whatsapp';
import { SharedModule } from '../shared/shared.module';

const routes: Routes = [
{
Expand All @@ -22,6 +23,7 @@ const routes: Routes = [
CommonModule,
RouterModule.forChild(routes),
WhatsappModule,
SharedModule,
ApolloModule,
LoonaModule.forRoot(),
],
Expand Down
23 changes: 23 additions & 0 deletions client/src/app/graphql/queries/fragments.graphql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import gql from 'graphql-tag';

export const UserFragment = gql`
fragment UserFragment on User {
id
name
}
`;

export const MessageFragment = gql`
fragment MessageFragment on Message {
id
text
createdAt
sender @type(name: "User") {
...UserFragment
}
recipient @type(name: "User") {
...UserFragment
}
}
${UserFragment}
`;
19 changes: 19 additions & 0 deletions client/src/app/graphql/queries/get-chats.graphql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import gql from 'graphql-tag';

import { UserFragment, MessageFragment } from './fragments.graphql';

export default gql`
{
chats @rest(type: "Chat", path: "/chats") {
id @export(as: "chatId")
members @rest(type: "[User]", path: "/chats/:chatId/members") {
...UserFragment
}
recentMessage @type(name: "Message") {
...MessageFragment
}
}
}
${UserFragment}
${MessageFragment}
`;
68 changes: 68 additions & 0 deletions client/src/app/ngrx/chats.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { of, combineLatest } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';

function api(path: string) {
return `http://localhost:4000${path}`;
}

@Injectable()
export class ChatsService {
constructor(private http: HttpClient) {}

getChats() {
this.fetchChats().pipe(
mergeMap(chats =>
combineLatest(chats.map(chat => this.resolveChat(chat))),
),
);
}

private fetchChats() {
return this.http.get<ChatsResponse>(api('/chats'));
}

private fetchUser(link: string) {
return this.http.get<UserResponse>(link);
}

private resolveChat(chat: ChatResponse) {
return combineLatest(chat.members.map(link => this.fetchUser(link))).pipe(
map(members => {
return {
...chat,
messages: [],
members,
};
}),
);
}
}

export type Link = string;
export type ID = string;

export interface ChatResponse {
id: ID;
members: Link[];
messages: Link;
recentMessage: MessageResponse;
}

export type ChatsResponse = ChatResponse[];

export interface UserResponse {
id: ID;
name: string;
}

export type ChatMessagesResponse = MessageResponse[];

export interface MessageResponse {
id: ID;
text: string;
createdAt: string;
sender: UserResponse;
recipient: UserResponse;
}
11 changes: 9 additions & 2 deletions client/src/app/ngrx/ngrx-root.component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Component } from '@angular/core';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';

import { Chat, Pages, PageChangeEvent, MessageEvent, ID } from '../whatsapp';
import { LoadChats } from './state/chat.actions';
import { AppState } from './app.state';

@Component({
selector: 'app-ngrx-root',
Expand All @@ -22,7 +26,7 @@ export class NgRxRootComponent {
chats: Observable<Chat[]>;
chat: Observable<Chat>;

constructor() {}
constructor(private store: Store<AppState>) {}

onPage(event: PageChangeEvent) {
this.page = event.page;
Expand All @@ -46,7 +50,10 @@ export class NgRxRootComponent {

toggleStar(chatId: ID) {}

loadChats() {}
loadChats() {
this.store.dispatch(new LoadChats());
this.chats = this.store.select(state => state.chats);
}

loadChat(id: ID) {}
}
14 changes: 11 additions & 3 deletions client/src/app/ngrx/ngrx.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ import { StoreModule } from '@ngrx/store';
import { EffectsModule } from '@ngrx/effects';

import { NgRxRootComponent } from './ngrx-root.component';
import { SharedModule } from '../shared/shared.module';
import { WhatsappModule } from '../whatsapp';
import { SharedModule } from '../shared/shared.module';
import { chatReducer } from './state/chat.reducer';
import { ChatEffects } from './state/chat.effects';
import { ChatsService } from './chats.service';
import { AppState } from './app.state';

const routes: Routes = [
{
Expand All @@ -20,9 +24,13 @@ const routes: Routes = [
imports: [
CommonModule,
RouterModule.forChild(routes),
SharedModule,
WhatsappModule,
SharedModule,
StoreModule.forRoot<AppState>({
chats: chatReducer,
}),
EffectsModule.forRoot([ChatEffects]),
],
providers: [],
providers: [ChatsService],
})
export class NgRxModule {}
29 changes: 29 additions & 0 deletions client/src/app/ngrx/state/chat.actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Action } from '@ngrx/store';
import { Chat, Message, ID } from '../../whatsapp';

export enum ActionTypes {
LoadChats = '[Chats] Load',
LoadChatsSuccess = '[Chats] Load Success',
LoadChatsFailure = '[Chats] Load Failure',
}

// All chats

export class LoadChats implements Action {
readonly type = ActionTypes.LoadChats;
}

export class LoadChatsSuccess implements Action {
readonly type = ActionTypes.LoadChatsSuccess;
constructor(
public payload: {
chats: Chat[];
},
) {}
}

export class LoadChatsFailure implements Action {
readonly type = ActionTypes.LoadChatsFailure;
}

export type ChatAction = LoadChats | LoadChatsSuccess | LoadChatsFailure;
34 changes: 34 additions & 0 deletions client/src/app/ngrx/state/chat.effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { Effect, Actions, ofType } from '@ngrx/effects';
import { Observable, of, combineLatest } from 'rxjs';
import { mergeMap, catchError, first, take } from 'rxjs/operators';
import { Store } from '@ngrx/store';
import { AppState } from '../app.state';
import {
ChatAction,
ActionTypes,
LoadChatsSuccess,
LoadChatsFailure,
} from './chat.actions';
import { User } from '../../whatsapp';
import { ChatsService } from '../chats.service';

@Injectable()
export class ChatEffects {
constructor(
protected actions$: Actions<ChatAction>,
protected store$: Store<AppState>,
protected chats: ChatsService,
) {}

@Effect()
loadChats$: Observable<ChatAction> = this.actions$.pipe(
ofType(ActionTypes.LoadChats),
mergeMap(_ => {
return this.chats.getChats().pipe(
mergeMap(chats => of(new LoadChatsSuccess({ chats }))),
catchError(() => of(new LoadChatsFailure())),
);
}),
);
}
20 changes: 20 additions & 0 deletions client/src/app/ngrx/state/chat.reducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ChatAction, ActionTypes } from './chat.actions';
import { ChatState } from './chat.state';

export const initialState: ChatState = [];

export function chatReducer(
state = initialState,
action: ChatAction,
): ChatState {
switch (action.type) {
case ActionTypes.LoadChatsSuccess: {
const { chats } = action.payload;

return chats;
}

default:
return state;
}
}

0 comments on commit c84fb58

Please sign in to comment.