Browse code

[add] handling for "user_renamed" event

Christian Fraß authored on 20/11/2021 19:12:53
Showing 1 changed files
... ...
@@ -101,6 +101,30 @@ namespace ns_model
101 101
 					shall_update_users = true;
102 102
 					break;
103 103
 				}
104
+				case "user_renamed":
105
+				{
106
+					for (let [channel_name, channel] of Object.entries(model.channels))
107
+					{
108
+						for (let user of channel.users)
109
+						{
110
+							if (user.name === event.data["user_name_old"])
111
+							{
112
+								user.name = event.data["user_name_new"];
113
+								channel.entries.push
114
+								({
115
+									"timestamp": event.timestamp,
116
+									"kind": enum_entrykind.info,
117
+									"sender": null,
118
+									"content": `${event.data["user_name_old"]} renamed to ${event.data["user_name_new"]}`,
119
+								});
120
+								shall_update_entries = true;
121
+								shall_update_users = true;
122
+								break;
123
+							}
124
+						}
125
+					}
126
+					break;
127
+				}
104 128
 				case "user_joined":
105 129
 				{
106 130
 					if (model.channels.hasOwnProperty(event.data["channel"]))
Browse code

[mod] auto focus on message input

Christian Fraß authored on 20/11/2021 19:04:25
Showing 1 changed files
... ...
@@ -406,7 +406,7 @@ namespace ns_model
406 406
 		if ((user_name !== model.nickname) && (! model.queries.hasOwnProperty(user_name)))
407 407
 		{
408 408
 			model.queries[user_name] = {"entries": []};
409
-			notify(model, "spots_changed");
409
+			set_active(model, {"kind": "query", "name": user_name});
410 410
 		}
411 411
 		else
412 412
 		{
Browse code

[add] handling of "topic" events

Christian Fraß authored on 20/11/2021 18:48:59
Showing 1 changed files
... ...
@@ -193,6 +193,26 @@ namespace ns_model
193 193
 					}
194 194
 					break;
195 195
 				}
196
+				case "topic":
197
+				{
198
+					if (model.channels.hasOwnProperty(event.data["channel"]))
199
+					{
200
+						// model.channels[event.data["channel"]].topic = event.data["content"];
201
+						model.channels[event.data["channel"]].entries.push
202
+						({
203
+							"timestamp": event.timestamp,
204
+							"kind": enum_entrykind.info,
205
+							"sender": null,
206
+							"content": `channel topic: ${event.data["content"]}`,
207
+						});
208
+						shall_update_entries = true;
209
+					}
210
+					else
211
+					{
212
+						// do nothing
213
+					}
214
+					break;
215
+				}
196 216
 				case "message_channel":
197 217
 				{
198 218
 					model.channels[event.data["channel"]].entries.push
Browse code

[mod] wait for server side confirmation for a message

Christian Fraß authored on 20/11/2021 18:48:27
Showing 1 changed files
... ...
@@ -199,7 +199,7 @@ namespace ns_model
199 199
 					({
200 200
 						"timestamp": event.timestamp,
201 201
 						"kind": enum_entrykind.message,
202
-						"sender": event.data["sender"],
202
+						"sender": (event.data["sender"] ?? model.nickname),
203 203
 						"content": event.data["content"],
204 204
 					});
205 205
 					shall_update_entries = true;
... ...
@@ -220,7 +220,7 @@ namespace ns_model
220 220
 					({
221 221
 						"timestamp": event.timestamp,
222 222
 						"kind": enum_entrykind.message,
223
-						"sender": event.data["sender"],
223
+						"sender": (event.data["sender"] ?? model.nickname),
224 224
 						"content": event.data["content"],
225 225
 					});
226 226
 					shall_update_entries = true;
... ...
@@ -322,6 +322,7 @@ namespace ns_model
322 322
 						"content": content,
323 323
 					}
324 324
 				);
325
+				/*
325 326
 				const event: type_event =
326 327
 				{
327 328
 					"timestamp": get_timestamp(),
... ...
@@ -335,6 +336,7 @@ namespace ns_model
335 336
 				};
336 337
 				process_events(model, [event]);
337 338
 				notify(model, "entries_changed");
339
+				 */
338 340
 				notify(model, "message_sent");
339 341
 				break;
340 342
 			}
... ...
@@ -350,6 +352,7 @@ namespace ns_model
350 352
 						"content": content,
351 353
 					}
352 354
 				);
355
+				/*
353 356
 				const event: type_event =
354 357
 				{
355 358
 					"timestamp": get_timestamp(),
... ...
@@ -363,6 +366,7 @@ namespace ns_model
363 366
 				};
364 367
 				process_events(model, [event]);
365 368
 				notify(model, "entries_changed");
369
+				 */
366 370
 				notify(model, "message_sent");
367 371
 				break;
368 372
 			}
Browse code

[fix] info entries

Christian Fraß authored on 20/11/2021 16:34:37
Showing 1 changed files
... ...
@@ -103,50 +103,71 @@ namespace ns_model
103 103
 				}
104 104
 				case "user_joined":
105 105
 				{
106
-					model.channels[event.data["channel"]].users.push({"name": event.data["user_name"], "role": ""});
107
-					model.channels[event.data["channel"]].entries.push
108
-					({
109
-						"timestamp": event.timestamp,
110
-						"kind": enum_entrykind.info,
111
-						"sender": null,
112
-						"content": `${event.data["user_name"]} joined`,
113
-					});
114
-					shall_update_users = true;
115
-					shall_update_entries = true;
106
+					if (model.channels.hasOwnProperty(event.data["channel"]))
107
+					{
108
+						model.channels[event.data["channel"]].users.push({"name": event.data["user_name"], "role": ""});
109
+						model.channels[event.data["channel"]].entries.push
110
+						({
111
+							"timestamp": event.timestamp,
112
+							"kind": enum_entrykind.info,
113
+							"sender": null,
114
+							"content": `${event.data["user_name"]} joined`,
115
+						});
116
+						shall_update_users = true;
117
+						shall_update_entries = true;
118
+					}
119
+					else
120
+					{
121
+						// do nothing
122
+					}
116 123
 					break;
117 124
 				}
118 125
 				case "user_parted":
119 126
 				{
120
-					model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
121
-					(
122
-						(user) => (user.name != event.data["user_name"])
123
-					);
124
-					model.channels[event.data["channel"]].entries.push
125
-					({
126
-						"timestamp": event.timestamp,
127
-						"kind": enum_entrykind.info,
128
-						"sender": null,
129
-						"content": `${event.data["user_name"]} left`,
130
-					});
131
-					shall_update_users = true;
132
-					shall_update_entries = true;
127
+					if (model.channels.hasOwnProperty(event.data["channel"]))
128
+					{
129
+						model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
130
+						(
131
+							(user) => (user.name != event.data["user_name"])
132
+						);
133
+						model.channels[event.data["channel"]].entries.push
134
+						({
135
+							"timestamp": event.timestamp,
136
+							"kind": enum_entrykind.info,
137
+							"sender": null,
138
+							"content": `${event.data["user_name"]} left`,
139
+						});
140
+						shall_update_users = true;
141
+						shall_update_entries = true;
142
+					}
143
+					else
144
+					{
145
+						// do nothing
146
+					}
133 147
 					break;
134 148
 				}
135 149
 				case "user_kicked":
136 150
 				{
137
-					model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
138
-					(
139
-						(user) => (user.name != event.data["user_name"])
140
-					);
141
-					model.channels[event.data["channel"]].entries.push
142
-					({
143
-						"timestamp": event.timestamp,
144
-						"kind": enum_entrykind.info,
145
-						"sender": null,
146
-						"content": `${event.data["user_name"]} was kicked by ${event.data["op_name"]}: ${event.data["reason"]}`,
147
-					});
148
-					shall_update_users = true;
149
-					shall_update_entries = true;
151
+					if (model.channels.hasOwnProperty(event.data["channel"]))
152
+					{
153
+						model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
154
+						(
155
+							(user) => (user.name != event.data["user_name"])
156
+						);
157
+						model.channels[event.data["channel"]].entries.push
158
+						({
159
+							"timestamp": event.timestamp,
160
+							"kind": enum_entrykind.info,
161
+							"sender": null,
162
+							"content": `${event.data["user_name"]} was kicked by ${event.data["op_name"]}: ${event.data["reason"]}`,
163
+						});
164
+						shall_update_users = true;
165
+						shall_update_entries = true;
166
+					}
167
+					else
168
+					{
169
+						// do nothing
170
+					}
150 171
 					break;
151 172
 				}
152 173
 				case "user_quit":
... ...
@@ -155,21 +176,21 @@ namespace ns_model
155 176
 					{
156 177
 						if (model.channels.hasOwnProperty(channel_name))
157 178
 						{
158
-							model.channels[channel_name].users = model.channels[event.data["channel"]].users.filter
179
+							model.channels[channel_name].users = model.channels[channel_name].users.filter
159 180
 							(
160 181
 								(user) => (user.name != event.data["user_name"])
161 182
 							);
183
+							model.channels[channel_name].entries.push
184
+							({
185
+								"timestamp": event.timestamp,
186
+								"kind": enum_entrykind.info,
187
+								"sender": null,
188
+								"content": `${event.data["user_name"]} quit`,
189
+							});
190
+							shall_update_entries = true;
191
+							shall_update_users = true;
162 192
 						}
163
-						model.channels[channel_name].entries.push
164
-						({
165
-							"timestamp": event.timestamp,
166
-							"kind": enum_entrykind.info,
167
-							"sender": null,
168
-							"content": `${event.data["user_name"]} quit`,
169
-						});
170 193
 					}
171
-					shall_update_entries = true;
172
-					shall_update_users = true;
173 194
 					break;
174 195
 				}
175 196
 				case "message_channel":
Browse code

[mod] added view for info entries

Christian Fraß authored on 20/11/2021 16:01:55
Showing 1 changed files
... ...
@@ -104,7 +104,15 @@ namespace ns_model
104 104
 				case "user_joined":
105 105
 				{
106 106
 					model.channels[event.data["channel"]].users.push({"name": event.data["user_name"], "role": ""});
107
+					model.channels[event.data["channel"]].entries.push
108
+					({
109
+						"timestamp": event.timestamp,
110
+						"kind": enum_entrykind.info,
111
+						"sender": null,
112
+						"content": `${event.data["user_name"]} joined`,
113
+					});
107 114
 					shall_update_users = true;
115
+					shall_update_entries = true;
108 116
 					break;
109 117
 				}
110 118
 				case "user_parted":
... ...
@@ -113,7 +121,15 @@ namespace ns_model
113 121
 					(
114 122
 						(user) => (user.name != event.data["user_name"])
115 123
 					);
124
+					model.channels[event.data["channel"]].entries.push
125
+					({
126
+						"timestamp": event.timestamp,
127
+						"kind": enum_entrykind.info,
128
+						"sender": null,
129
+						"content": `${event.data["user_name"]} left`,
130
+					});
116 131
 					shall_update_users = true;
132
+					shall_update_entries = true;
117 133
 					break;
118 134
 				}
119 135
 				case "user_kicked":
... ...
@@ -122,7 +138,15 @@ namespace ns_model
122 138
 					(
123 139
 						(user) => (user.name != event.data["user_name"])
124 140
 					);
141
+					model.channels[event.data["channel"]].entries.push
142
+					({
143
+						"timestamp": event.timestamp,
144
+						"kind": enum_entrykind.info,
145
+						"sender": null,
146
+						"content": `${event.data["user_name"]} was kicked by ${event.data["op_name"]}: ${event.data["reason"]}`,
147
+					});
125 148
 					shall_update_users = true;
149
+					shall_update_entries = true;
126 150
 					break;
127 151
 				}
128 152
 				case "user_quit":
... ...
@@ -136,8 +160,15 @@ namespace ns_model
136 160
 								(user) => (user.name != event.data["user_name"])
137 161
 							);
138 162
 						}
163
+						model.channels[channel_name].entries.push
164
+						({
165
+							"timestamp": event.timestamp,
166
+							"kind": enum_entrykind.info,
167
+							"sender": null,
168
+							"content": `${event.data["user_name"]} quit`,
169
+						});
139 170
 					}
140
-					shall_update_spots = true;
171
+					shall_update_entries = true;
141 172
 					shall_update_users = true;
142 173
 					break;
143 174
 				}
... ...
@@ -146,6 +177,7 @@ namespace ns_model
146 177
 					model.channels[event.data["channel"]].entries.push
147 178
 					({
148 179
 						"timestamp": event.timestamp,
180
+						"kind": enum_entrykind.message,
149 181
 						"sender": event.data["sender"],
150 182
 						"content": event.data["content"],
151 183
 					});
... ...
@@ -166,6 +198,7 @@ namespace ns_model
166 198
 					model.queries[event.data["user_name"]].entries.push
167 199
 					({
168 200
 						"timestamp": event.timestamp,
201
+						"kind": enum_entrykind.message,
169 202
 						"sender": event.data["sender"],
170 203
 						"content": event.data["content"],
171 204
 					});
Browse code

[mod] support user join/part/kick/quit events

Christian Fraß authored on 20/11/2021 15:44:18
Showing 1 changed files
... ...
@@ -95,12 +95,52 @@ namespace ns_model
95 95
 					console.warn("unhandled event kind: " + event.kind);
96 96
 					break;
97 97
 				}
98
-				case "userlist":
98
+				case "user_list":
99 99
 				{
100 100
 					model.channels[event.data["channel"]].users = event.data["users"];
101 101
 					shall_update_users = true;
102 102
 					break;
103 103
 				}
104
+				case "user_joined":
105
+				{
106
+					model.channels[event.data["channel"]].users.push({"name": event.data["user_name"], "role": ""});
107
+					shall_update_users = true;
108
+					break;
109
+				}
110
+				case "user_parted":
111
+				{
112
+					model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
113
+					(
114
+						(user) => (user.name != event.data["user_name"])
115
+					);
116
+					shall_update_users = true;
117
+					break;
118
+				}
119
+				case "user_kicked":
120
+				{
121
+					model.channels[event.data["channel"]].users = model.channels[event.data["channel"]].users.filter
122
+					(
123
+						(user) => (user.name != event.data["user_name"])
124
+					);
125
+					shall_update_users = true;
126
+					break;
127
+				}
128
+				case "user_quit":
129
+				{
130
+					for (let channel_name of event.data["channels"])
131
+					{
132
+						if (model.channels.hasOwnProperty(channel_name))
133
+						{
134
+							model.channels[channel_name].users = model.channels[event.data["channel"]].users.filter
135
+							(
136
+								(user) => (user.name != event.data["user_name"])
137
+							);
138
+						}
139
+					}
140
+					shall_update_spots = true;
141
+					shall_update_users = true;
142
+					break;
143
+				}
104 144
 				case "message_channel":
105 145
 				{
106 146
 					model.channels[event.data["channel"]].entries.push
Browse code

[add] possibility to open queries

Christian Fraß authored on 20/11/2021 15:20:07
Showing 1 changed files
... ...
@@ -276,6 +276,27 @@ namespace ns_model
276 276
 	}
277 277
 	
278 278
 	
279
+	/**
280
+	 * adds a query to a user as spot
281
+	 */
282
+	export function open_query
283
+	(
284
+		model: type_model,
285
+		user_name: string
286
+	): void
287
+	{
288
+		if ((user_name !== model.nickname) && (! model.queries.hasOwnProperty(user_name)))
289
+		{
290
+			model.queries[user_name] = {"entries": []};
291
+			notify(model, "spots_changed");
292
+		}
293
+		else
294
+		{
295
+			// do nothing
296
+		}
297
+	}
298
+	
299
+	
279 300
 	/**
280 301
 	 * sets up the model
281 302
 	 */
Browse code

[mov] source/*.ts -> source/logic/*.ts

Christian Fraß authored on 20/11/2021 15:04:11
Showing 1 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,331 @@
1
+namespace ns_model
2
+{
3
+	
4
+	/**
5
+	 * adds a listener for a certain incident
6
+	 */
7
+	export function listen
8
+	(
9
+		model: type_model,
10
+		incident: string,
11
+		handler: (details?: any)=>void
12
+	): void
13
+	{
14
+		if (! model.listeners.hasOwnProperty(incident))
15
+		{
16
+			model.listeners[incident] = [];
17
+		}
18
+		else
19
+		{
20
+			// do nothing
21
+		}
22
+		model.listeners[incident].push(handler);
23
+	}
24
+
25
+
26
+	/**
27
+	 * sends a notification to all listeners for a certain incident
28
+	 */
29
+	function notify
30
+	(
31
+		model: type_model,
32
+		incident: string,
33
+		details: any = null
34
+	): void
35
+	{
36
+		if (model.listeners.hasOwnProperty(incident))
37
+		{
38
+			for (const handler of model.listeners[incident])
39
+			{
40
+				handler(details);
41
+			}
42
+		}
43
+	}
44
+
45
+
46
+	/**
47
+	 * sets the state
48
+	 */
49
+	export function set_state
50
+	(
51
+		model: type_model,
52
+		state: enum_state
53
+	): void
54
+	{
55
+		model.state = state;
56
+		notify(model, "state_changed");
57
+	}
58
+
59
+
60
+	/**
61
+	 * sets the active spot (channel or query)
62
+	 */
63
+	export function set_active
64
+	(
65
+		model: type_model,
66
+		spot: type_spot
67
+	): void
68
+	{
69
+		model.active = spot;
70
+		notify(model, "spots_changed");
71
+		notify(model, "entries_changed");
72
+		notify(model, "users_changed");
73
+	}
74
+
75
+
76
+	/**
77
+	 * updates the model according to a list of events
78
+	 */
79
+	function process_events
80
+	(
81
+		model: type_model,
82
+		events: Array<type_event>
83
+	): void
84
+	{
85
+		let shall_update_spots: boolean = false;
86
+		let shall_update_entries: boolean = false;
87
+		let shall_update_users: boolean = false;
88
+		
89
+		for (const event of events)
90
+		{
91
+			switch (event.kind)
92
+			{
93
+				default:
94
+				{
95
+					console.warn("unhandled event kind: " + event.kind);
96
+					break;
97
+				}
98
+				case "userlist":
99
+				{
100
+					model.channels[event.data["channel"]].users = event.data["users"];
101
+					shall_update_users = true;
102
+					break;
103
+				}
104
+				case "message_channel":
105
+				{
106
+					model.channels[event.data["channel"]].entries.push
107
+					({
108
+						"timestamp": event.timestamp,
109
+						"sender": event.data["sender"],
110
+						"content": event.data["content"],
111
+					});
112
+					shall_update_entries = true;
113
+					break;
114
+				}
115
+				case "message_query":
116
+				{
117
+					if (! model.queries.hasOwnProperty(event.data["user_name"]))
118
+					{
119
+						model.queries[event.data["user_name"]] = {"entries": []};
120
+						shall_update_spots = true;
121
+					}
122
+					else
123
+					{
124
+						// do nothing
125
+					}
126
+					model.queries[event.data["user_name"]].entries.push
127
+					({
128
+						"timestamp": event.timestamp,
129
+						"sender": event.data["sender"],
130
+						"content": event.data["content"],
131
+					});
132
+					shall_update_entries = true;
133
+					break;
134
+				}
135
+			}
136
+		}
137
+		
138
+		if (shall_update_spots) notify(model, "spots_changed");
139
+		if (shall_update_entries) notify(model, "entries_changed");
140
+		if (shall_update_users) notify(model, "users_changed");
141
+	}
142
+
143
+
144
+	/**
145
+	 * establishes the connection
146
+	 */
147
+	export async function connect
148
+	(
149
+		conf: type_conf,
150
+		model: type_model,
151
+		nickname: string,
152
+		channel_names: Array<string>
153
+	): Promise<void>
154
+	{
155
+		set_state(model, enum_state.connecting);
156
+		const connection_id: string = await backend_call
157
+		(
158
+			conf,
159
+			model.connection_id,
160
+			"connect",
161
+			{
162
+				"server": conf.irc.server,
163
+				"channels": channel_names,
164
+				"nickname": nickname,
165
+			}
166
+		);
167
+		model.connection_id = connection_id;
168
+		model.nickname = nickname;
169
+		for (const channel_name of channel_names)
170
+		{
171
+			model.channels[channel_name] =
172
+			{
173
+				"users": [],
174
+				"entries": [],
175
+			};
176
+		}
177
+		if (channel_names.length > 0)
178
+		{
179
+			set_active(model, {"kind": "channel", "name": channel_names[0]});
180
+		}
181
+		return Promise.resolve<void>(undefined);
182
+	}
183
+
184
+
185
+	/**
186
+	 * closes the connection
187
+	 */
188
+	export async function disconnect
189
+	(
190
+		conf: type_conf,
191
+		model: type_model
192
+	): Promise<void>
193
+	{
194
+		await backend_call
195
+		(
196
+			conf,
197
+			model.connection_id,
198
+			"disconnect",
199
+			null
200
+		);
201
+		set_state(model, enum_state.offline);
202
+		model.connection_id = null;
203
+		return Promise.resolve<void>(undefined);
204
+	}
205
+
206
+
207
+	/**
208
+	 * adds a client side message
209
+	 */
210
+	export function send
211
+	(
212
+		conf: type_conf,
213
+		model: type_model,
214
+		content: string
215
+	): void
216
+	{
217
+		switch (model.active.kind)
218
+		{
219
+			case "channel":
220
+			{
221
+				backend_call
222
+				(
223
+					conf,
224
+					model.connection_id,
225
+					"send_channel",
226
+					{
227
+						"channel": model.active.name,
228
+						"content": content,
229
+					}
230
+				);
231
+				const event: type_event =
232
+				{
233
+					"timestamp": get_timestamp(),
234
+					"kind": "message_channel",
235
+					"data":
236
+					{
237
+						"channel": model.active.name,
238
+						"sender": model.nickname,
239
+						"content": content,
240
+					}
241
+				};
242
+				process_events(model, [event]);
243
+				notify(model, "entries_changed");
244
+				notify(model, "message_sent");
245
+				break;
246
+			}
247
+			case "query":
248
+			{
249
+				backend_call
250
+				(
251
+					conf,
252
+					model.connection_id,
253
+					"send_query",
254
+					{
255
+						"receiver": model.active.name,
256
+						"content": content,
257
+					}
258
+				);
259
+				const event: type_event =
260
+				{
261
+					"timestamp": get_timestamp(),
262
+					"kind": "message_query",
263
+					"data":
264
+					{
265
+						"user_name": model.active.name,
266
+						"sender": model.nickname,
267
+						"content": content,
268
+					}
269
+				};
270
+				process_events(model, [event]);
271
+				notify(model, "entries_changed");
272
+				notify(model, "message_sent");
273
+				break;
274
+			}
275
+		}
276
+	}
277
+	
278
+	
279
+	/**
280
+	 * sets up the model
281
+	 */
282
+	export function setup
283
+	(
284
+		conf: type_conf,
285
+		model: type_model
286
+	): void
287
+	{
288
+		setInterval
289
+		(
290
+			async () =>
291
+			{
292
+				switch (model.state)
293
+				{
294
+					default:
295
+					{
296
+						throw (new Error(`invalid state '${model.state}'`));
297
+						break;
298
+					}
299
+					case enum_state.offline:
300
+					{
301
+						// do nothing
302
+						break;
303
+					}
304
+					case enum_state.connecting:
305
+					{
306
+						const ready: boolean = await backend_call(conf, model.connection_id, "check", null);
307
+						if (ready)
308
+						{
309
+							set_state(model, enum_state.online);
310
+						}
311
+						else
312
+						{
313
+							// do nothing
314
+						}
315
+						break;
316
+					}
317
+					case enum_state.online:
318
+					{
319
+						const events: Array<type_event> = await backend_call(conf, model.connection_id, "fetch", null);
320
+						process_events(model, events);
321
+						break;
322
+					}
323
+				}
324
+			},
325
+			conf.settings.poll_interval_in_milliseconds
326
+		);
327
+		set_state(model, enum_state.offline);
328
+	}
329
+	
330
+}
331
+