-
Notifications
You must be signed in to change notification settings - Fork 87
/
threads.reducer.ts
158 lines (134 loc) · 4.79 KB
/
threads.reducer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/* tslint:disable no-switch-case-fall-through */
import { Action } from 'redux';
import { createSelector } from 'reselect';
import { Thread } from './thread.model';
import { Message } from '../message/message.model';
import * as ThreadActions from './thread.actions';
/**
* This file describes the state concerning Threads, how to modify them through
* the reducer, and how to query the state via selectors.
*
* ThreadsState stores the list of Threads indexed by id in `entities`, as well
* as a complete list of the ids in `ids`.
*
* We also store the id of the current thread so that we know what the user is
* currently looking at - this is valuable for the unread messages count, for
* instance.
*
* In this app, we store the Messages in their respective Thread and we don't
* store the Messages apart from that Thread. In your app you may find it useful
* to separate Messages into their own Messages reducer and keep only a list
* of Message ids in your Threads.
*/
export interface ThreadsEntities {
[id: string]: Thread;
}
export interface ThreadsState {
ids: string[];
entities: ThreadsEntities;
currentThreadId?: string;
};
const initialState: ThreadsState = {
ids: [],
currentThreadId: null,
entities: {}
};
/**
* The `ThreadsReducer` describes how to modify the `ThreadsState` given a
* particular action.
*/
export const ThreadsReducer =
function(state: ThreadsState = initialState, action: Action): ThreadsState {
switch (action.type) {
// Adds a new Thread to the list of entities
case ThreadActions.ADD_THREAD: {
const thread = (<ThreadActions.AddThreadAction>action).thread;
if (state.ids.includes(thread.id)) {
return state;
}
return {
ids: [ ...state.ids, thread.id ],
currentThreadId: state.currentThreadId,
entities: Object.assign({}, state.entities, {
[thread.id]: thread
})
};
}
// Adds a new Message to a particular Thread
case ThreadActions.ADD_MESSAGE: {
const thread = (<ThreadActions.AddMessageAction>action).thread;
const message = (<ThreadActions.AddMessageAction>action).message;
// special case: if the message being added is in the current thread, then
// mark it as read
const isRead = message.thread.id === state.currentThreadId ?
true : message.isRead;
const newMessage = Object.assign({}, message, { isRead: isRead });
// grab the old thraed from entities
const oldThread = state.entities[thread.id];
// create a new thread which has our newMessage
const newThread = Object.assign({}, oldThread, {
messages: [...oldThread.messages, newMessage]
});
return {
ids: state.ids, // unchanged
currentThreadId: state.currentThreadId, // unchanged
entities: Object.assign({}, state.entities, {
[thread.id]: newThread
})
};
}
// Select a particular thread in the UI
case ThreadActions.SELECT_THREAD: {
const thread = (<ThreadActions.SelectThreadAction>action).thread;
const oldThread = state.entities[thread.id];
// mark the messages as read
const newMessages = oldThread.messages.map(
(message) => Object.assign({}, message, { isRead: true }));
// give them to this new thread
const newThread = Object.assign({}, oldThread, {
messages: newMessages
});
return {
ids: state.ids,
currentThreadId: thread.id,
entities: Object.assign({}, state.entities, {
[thread.id]: newThread
})
};
}
default:
return state;
}
};
export const getThreadsState = (state): ThreadsState => state.threads;
export const getThreadsEntities = createSelector(
getThreadsState,
( state: ThreadsState ) => state.entities );
export const getAllThreads = createSelector(
getThreadsEntities,
( entities: ThreadsEntities ) => Object.keys(entities)
.map((threadId) => entities[threadId]));
export const getUnreadMessagesCount = createSelector(
getAllThreads,
( threads: Thread[] ) => threads.reduce(
(unreadCount: number, thread: Thread) => {
thread.messages.forEach((message: Message) => {
if (!message.isRead) {
++unreadCount;
}
});
return unreadCount;
},
0));
// This selector emits the current thread
export const getCurrentThread = createSelector(
getThreadsEntities,
getThreadsState,
( entities: ThreadsEntities, state: ThreadsState ) =>
entities[state.currentThreadId] );
export const getAllMessages = createSelector(
getAllThreads,
( threads: Thread[] ) =>
threads.reduce( // gather all messages
(messages, thread) => [...messages, ...thread.messages],
[]).sort((m1, m2) => m1.sentAt - m2.sentAt)); // sort them by time