Christian Fraß commited on 2021-11-19 13:04:04
Zeige 3 geänderte Dateien mit 183 Einfügungen und 95 Löschungen.
| ... | ... |
@@ -2,10 +2,11 @@ |
| 2 | 2 |
<html> |
| 3 | 3 |
<head> |
| 4 | 4 |
<meta charset="utf-8"/> |
| 5 |
- <title>web-irc</title> |
|
| 5 |
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
| 6 | 6 |
<link rel="stylesheet" type="text/css" href="style.css"/> |
| 7 | 7 |
<script type="text/javascript" src="logic.js"></script> |
| 8 | 8 |
<script type="text/javascript">init();</script> |
| 9 |
+ <title>wirc</title> |
|
| 9 | 10 |
</head> |
| 10 | 11 |
<body class="offline"> |
| 11 | 12 |
<div id="connect"> |
| ... | ... |
@@ -26,15 +27,20 @@ |
| 26 | 27 |
</div> |
| 27 | 28 |
<div id="main"> |
| 28 | 29 |
<div id="head"> |
| 30 |
+ <div id="head_left"> |
|
| 31 |
+ <h2>wirc</h2> |
|
| 32 |
+ </div> |
|
| 33 |
+ <div id="head_right"> |
|
| 29 | 34 |
<button id="disconnect">disconnect</button> |
| 30 | 35 |
</div> |
| 36 |
+ </div> |
|
| 31 | 37 |
<div id="middle"> |
| 32 |
- <ul class="pane" id="history"></ul> |
|
| 38 |
+ <ul class="pane" id="events"></ul> |
|
| 33 | 39 |
<ul class="pane" id="users"></ul> |
| 34 | 40 |
</div> |
| 35 | 41 |
<form action="#"> |
| 36 | 42 |
<input type="text" id="message" placeholder="…"/> |
| 37 |
- <input type="submit" value="send"/> |
|
| 43 |
+ <input type="submit" id="send" value="send"/> |
|
| 38 | 44 |
</form> |
| 39 | 45 |
</template> |
| 40 | 46 |
</body> |
| ... | ... |
@@ -3,11 +3,6 @@ type float = number; |
| 3 | 3 |
|
| 4 | 4 |
|
| 5 | 5 |
var _conf: any = null; |
| 6 |
-var _state: string = undefined; |
|
| 7 |
-var _channel: string = undefined; |
|
| 8 |
-var _nickname: string = undefined; |
|
| 9 |
-var _userhash: string = undefined; |
|
| 10 |
-var _id: (null | string) = null; |
|
| 11 | 6 |
|
| 12 | 7 |
|
| 13 | 8 |
function get_timestamp(): int |
| ... | ... |
@@ -31,41 +26,76 @@ function get_usercolor(name: string): string |
| 31 | 26 |
|
| 32 | 27 |
async function backend_call(action: string, data: any): Promise<any> |
| 33 | 28 |
{
|
| 34 |
- const response = await fetch( |
|
| 29 |
+ const response = await fetch |
|
| 30 |
+ ( |
|
| 35 | 31 |
`${_conf.backend.scheme}://${_conf.backend.host}:${_conf.backend.port.toFixed(0)}`,
|
| 36 | 32 |
{
|
| 37 | 33 |
"method": "POST", |
| 38 |
- "body": JSON.stringify({"action": action, "id": _id, "data": data}),
|
|
| 34 |
+ "body": JSON.stringify({"action": action, "id": _model.connection_id, "data": data}),
|
|
| 39 | 35 |
} |
| 40 | 36 |
); |
| 41 |
- if (response.ok) {
|
|
| 37 |
+ if (response.ok) |
|
| 38 |
+ {
|
|
| 42 | 39 |
return response.json(); |
| 43 | 40 |
} |
| 44 |
- else {
|
|
| 41 |
+ else |
|
| 42 |
+ {
|
|
| 45 | 43 |
console.error(response.text()); |
| 46 | 44 |
return Promise.reject<any>(new Error("backend call failed"));
|
| 47 | 45 |
} |
| 48 | 46 |
} |
| 49 | 47 |
|
| 48 |
+enum enum_state |
|
| 49 |
+{
|
|
| 50 |
+ offline = "offline", |
|
| 51 |
+ connecting = "connecting", |
|
| 52 |
+ online = "online", |
|
| 53 |
+} |
|
| 54 |
+ |
|
| 55 |
+ |
|
| 56 |
+type type_model = |
|
| 57 |
+{
|
|
| 58 |
+ state: enum_state; |
|
| 59 |
+ channel: (null | string); |
|
| 60 |
+ nickname: (null | string); |
|
| 61 |
+ connection_id: (null | string); |
|
| 62 |
+ usershash: (null | string); |
|
| 63 |
+}; |
|
| 64 |
+ |
|
| 65 |
+ |
|
| 66 |
+var _model: type_model = |
|
| 67 |
+{
|
|
| 68 |
+ "state": enum_state.offline, |
|
| 69 |
+ "channel": null, |
|
| 70 |
+ "nickname": null, |
|
| 71 |
+ "connection_id": null, |
|
| 72 |
+ "usershash": null, |
|
| 73 |
+}; |
|
| 74 |
+ |
|
| 50 | 75 |
|
| 51 | 76 |
function update_state(): void |
| 52 | 77 |
{
|
| 53 |
- document.querySelector("body").setAttribute("class", _state);
|
|
| 78 |
+ document.querySelector("body").setAttribute("class", _model.state);
|
|
| 54 | 79 |
} |
| 55 | 80 |
|
| 56 | 81 |
|
| 57 |
-function update_history(events): void |
|
| 82 |
+function update_events(events): void |
|
| 83 |
+{
|
|
| 84 |
+ let dom_events: HTMLUListElement = document.querySelector("#events");
|
|
| 85 |
+ for (const event of events) |
|
| 58 | 86 |
{
|
| 59 |
- let dom_history: HTMLUListElement = document.querySelector("#history");
|
|
| 60 |
- for (const event of events) {
|
|
| 61 | 87 |
const timestring: string = (new Date(event["timestamp"]*1000)).toISOString().slice(11, 19); |
| 62 | 88 |
let dom_event: HTMLLIElement = document.createElement("li");
|
| 63 | 89 |
dom_event.classList.add("event");
|
| 64 |
- switch (event["kind"]) {
|
|
| 90 |
+ switch (event["kind"]) |
|
| 91 |
+ {
|
|
| 65 | 92 |
default: |
| 93 |
+ {
|
|
| 66 | 94 |
dom_event.textContent = ("-- unhandled event: " + JSON.stringify(event));
|
| 67 | 95 |
break; |
| 96 |
+ } |
|
| 68 | 97 |
case "channel_message": |
| 98 |
+ {
|
|
| 69 | 99 |
{
|
| 70 | 100 |
let dom_time: HTMLDivElement = document.createElement("div");
|
| 71 | 101 |
dom_time.classList.add("event_time");
|
| ... | ... |
@@ -87,9 +117,10 @@ function update_history(events): void |
| 87 | 117 |
} |
| 88 | 118 |
break; |
| 89 | 119 |
} |
| 90 |
- dom_history.appendChild(dom_event); |
|
| 91 | 120 |
} |
| 92 |
- dom_history.scrollTo(0, dom_history["scrollTopMax"]); |
|
| 121 |
+ dom_events.appendChild(dom_event); |
|
| 122 |
+ } |
|
| 123 |
+ dom_events.scrollTo(0, dom_events["scrollTopMax"]); |
|
| 93 | 124 |
} |
| 94 | 125 |
|
| 95 | 126 |
|
| ... | ... |
@@ -97,8 +128,10 @@ function update_users(users: Array<{name: string; role: string;}>): void
|
| 97 | 128 |
{
|
| 98 | 129 |
let dom_users: HTMLUListElement = document.querySelector("#users");
|
| 99 | 130 |
dom_users.textContent = ""; |
| 100 |
- const users_sorted: Array<{name: string; role: string;}> = users.sort(
|
|
| 101 |
- (x, y) => ( |
|
| 131 |
+ const users_sorted: Array<{name: string; role: string;}> = users.sort
|
|
| 132 |
+ ( |
|
| 133 |
+ (x, y) => |
|
| 134 |
+ ( |
|
| 102 | 135 |
(x.role >= y.role) |
| 103 | 136 |
? -1 |
| 104 | 137 |
: ( |
| ... | ... |
@@ -108,7 +141,8 @@ function update_users(users: Array<{name: string; role: string;}>): void
|
| 108 | 141 |
) |
| 109 | 142 |
) |
| 110 | 143 |
); |
| 111 |
- for (const user of users_sorted) {
|
|
| 144 |
+ for (const user of users_sorted) |
|
| 145 |
+ {
|
|
| 112 | 146 |
let dom_user: HTMLLIElement = document.createElement("li");
|
| 113 | 147 |
dom_user.classList.add("user");
|
| 114 | 148 |
{
|
| ... | ... |
@@ -122,15 +156,14 @@ function update_users(users: Array<{name: string; role: string;}>): void
|
| 122 | 156 |
dom_name.style.color = get_usercolor(user.name); |
| 123 | 157 |
dom_user.appendChild(dom_name); |
| 124 | 158 |
} |
| 125 |
- // dom_user.textContent = `${user.role}${user.name}`;
|
|
| 126 | 159 |
dom_users.appendChild(dom_user); |
| 127 | 160 |
} |
| 128 | 161 |
} |
| 129 | 162 |
|
| 130 | 163 |
|
| 131 |
-function set_state(state: string): void |
|
| 164 |
+function set_state(state: enum_state): void |
|
| 132 | 165 |
{
|
| 133 |
- _state = state; |
|
| 166 |
+ _model.state = state; |
|
| 134 | 167 |
update_state(); |
| 135 | 168 |
} |
| 136 | 169 |
|
| ... | ... |
@@ -139,45 +172,72 @@ function setup_view(): void |
| 139 | 172 |
{
|
| 140 | 173 |
document.querySelector<HTMLInputElement>("#channel").value = _conf["irc"]["predefined_channel"];
|
| 141 | 174 |
document.querySelector<HTMLInputElement>("#nickname").value = (_conf["irc"]["predefined_nickname_prefix"] + (Math.random()*100).toFixed(0));
|
| 142 |
- setInterval( |
|
| 143 |
- async () => {
|
|
| 144 |
- switch (_state) {
|
|
| 145 |
- case "offline": |
|
| 175 |
+ setInterval |
|
| 176 |
+ ( |
|
| 177 |
+ async () => |
|
| 178 |
+ {
|
|
| 179 |
+ switch (_model.state) |
|
| 180 |
+ {
|
|
| 181 |
+ default: |
|
| 182 |
+ {
|
|
| 183 |
+ throw (new Error("invalid state: " + _model.state));
|
|
| 184 |
+ break; |
|
| 185 |
+ } |
|
| 186 |
+ case enum_state.offline: |
|
| 187 |
+ {
|
|
| 146 | 188 |
// do nothing |
| 147 | 189 |
break; |
| 148 |
- case "checking": |
|
| 190 |
+ } |
|
| 191 |
+ case enum_state.connecting: |
|
| 192 |
+ {
|
|
| 149 | 193 |
const ready: boolean = await backend_call("check", null);
|
| 150 |
- if (ready) {
|
|
| 151 |
- set_state("online");
|
|
| 194 |
+ if (ready) |
|
| 195 |
+ {
|
|
| 196 |
+ set_state(enum_state.online); |
|
| 197 |
+ } |
|
| 198 |
+ else |
|
| 199 |
+ {
|
|
| 200 |
+ // do nothing |
|
| 152 | 201 |
} |
| 153 | 202 |
break; |
| 154 |
- case "online": |
|
| 203 |
+ } |
|
| 204 |
+ case enum_state.online: |
|
| 205 |
+ {
|
|
| 155 | 206 |
const stuff: any = await backend_call("fetch", null);
|
| 156 |
- update_history(stuff["events"]); |
|
| 157 |
- const userhash: string = btoa(JSON.stringify(stuff["users"])); |
|
| 158 |
- if (_userhash !== userhash) {
|
|
| 159 |
- _userhash = userhash; |
|
| 207 |
+ update_events(stuff["events"]); |
|
| 208 |
+ const usershash: string = btoa(JSON.stringify(stuff["users"])); |
|
| 209 |
+ if (_model.usershash !== usershash) |
|
| 210 |
+ {
|
|
| 211 |
+ _model.usershash = usershash; |
|
| 160 | 212 |
update_users(stuff["users"]); |
| 161 | 213 |
} |
| 214 |
+ else |
|
| 215 |
+ {
|
|
| 216 |
+ // do nothing |
|
| 217 |
+ } |
|
| 162 | 218 |
break; |
| 163 | 219 |
} |
| 220 |
+ } |
|
| 164 | 221 |
}, |
| 165 | 222 |
_conf["settings"]["poll_interval_in_milliseconds"] |
| 166 | 223 |
); |
| 167 |
- set_state("offline");
|
|
| 224 |
+ set_state(enum_state.offline); |
|
| 168 | 225 |
} |
| 169 | 226 |
|
| 170 | 227 |
|
| 171 | 228 |
function setup_control(): void |
| 172 | 229 |
{
|
| 173 |
- document.querySelector("#connect > form").addEventListener(
|
|
| 230 |
+ document.querySelector("#connect > form").addEventListener
|
|
| 231 |
+ ( |
|
| 174 | 232 |
"submit", |
| 175 |
- async (event) => {
|
|
| 233 |
+ async (event) => |
|
| 234 |
+ {
|
|
| 176 | 235 |
let dom_nickname: HTMLInputElement = document.querySelector<HTMLInputElement>("#nickname");
|
| 177 | 236 |
let dom_channel: HTMLInputElement = document.querySelector<HTMLInputElement>("#channel");
|
| 178 | 237 |
const nickname: string = dom_nickname.value; |
| 179 | 238 |
const channel: string = dom_channel.value; |
| 180 |
- const id: string = await backend_call( |
|
| 239 |
+ const connection_id: string = await backend_call |
|
| 240 |
+ ( |
|
| 181 | 241 |
"connect", |
| 182 | 242 |
{
|
| 183 | 243 |
"server": _conf["irc"]["server"], |
| ... | ... |
@@ -185,45 +245,52 @@ function setup_control(): void |
| 185 | 245 |
"nickname": nickname, |
| 186 | 246 |
} |
| 187 | 247 |
); |
| 188 |
- _id = id; |
|
| 189 |
- _channel = channel; |
|
| 190 |
- _nickname = nickname; |
|
| 191 |
- set_state("checking");
|
|
| 248 |
+ _model.connection_id = connection_id; |
|
| 249 |
+ _model.channel = channel; |
|
| 250 |
+ _model.nickname = nickname; |
|
| 251 |
+ set_state(enum_state.connecting); |
|
| 192 | 252 |
} |
| 193 | 253 |
); |
| 194 |
- document.querySelector("#disconnect").addEventListener(
|
|
| 254 |
+ document.querySelector("#disconnect").addEventListener
|
|
| 255 |
+ ( |
|
| 195 | 256 |
"click", |
| 196 |
- async (event) => {
|
|
| 197 |
- await backend_call( |
|
| 257 |
+ async (event) => |
|
| 258 |
+ {
|
|
| 259 |
+ await backend_call |
|
| 260 |
+ ( |
|
| 198 | 261 |
"disconnect", |
| 199 | 262 |
null |
| 200 | 263 |
); |
| 201 |
- set_state("offline");
|
|
| 202 |
- _id = null; |
|
| 264 |
+ set_state(enum_state.offline); |
|
| 265 |
+ _model.connection_id = null; |
|
| 203 | 266 |
} |
| 204 | 267 |
); |
| 205 |
- document.querySelector("#main > form").addEventListener(
|
|
| 268 |
+ document.querySelector("#main > form").addEventListener
|
|
| 269 |
+ ( |
|
| 206 | 270 |
"submit", |
| 207 |
- async (event) => {
|
|
| 271 |
+ async (event) => |
|
| 272 |
+ {
|
|
| 208 | 273 |
event.preventDefault(); |
| 209 | 274 |
let dom_message: HTMLInputElement = document.querySelector<HTMLInputElement>("#message");
|
| 210 | 275 |
const message: string = dom_message.value; |
| 211 | 276 |
dom_message.value = ""; |
| 212 | 277 |
dom_message.focus(); |
| 213 |
- const event_: any = {
|
|
| 278 |
+ const event_: any = |
|
| 279 |
+ {
|
|
| 214 | 280 |
"timestamp": get_timestamp(), |
| 215 | 281 |
"kind": "channel_message", |
| 216 | 282 |
"data": {
|
| 217 |
- "from": _nickname, |
|
| 218 |
- "to": _channel, |
|
| 283 |
+ "from": _model.nickname, |
|
| 284 |
+ "to": _model.channel, |
|
| 219 | 285 |
"message": message, |
| 220 | 286 |
} |
| 221 | 287 |
}; |
| 222 |
- update_history([event_]); |
|
| 223 |
- await backend_call( |
|
| 224 |
- "say", |
|
| 288 |
+ update_events([event_]); |
|
| 289 |
+ await backend_call |
|
| 290 |
+ ( |
|
| 291 |
+ "send", |
|
| 225 | 292 |
{
|
| 226 |
- "channel": _channel, |
|
| 293 |
+ "channel": _model.channel, |
|
| 227 | 294 |
"message": message, |
| 228 | 295 |
} |
| 229 | 296 |
); |
| ... | ... |
@@ -242,11 +309,10 @@ async function main(): Promise<void> |
| 242 | 309 |
|
| 243 | 310 |
function init(): void |
| 244 | 311 |
{
|
| 245 |
- document.addEventListener( |
|
| 312 |
+ document.addEventListener |
|
| 313 |
+ ( |
|
| 246 | 314 |
"DOMContentLoaded", |
| 247 |
- (event) => {
|
|
| 248 |
- main(); |
|
| 249 |
- } |
|
| 315 |
+ (event) => {main();}
|
|
| 250 | 316 |
); |
| 251 | 317 |
} |
| 252 | 318 |
|
| ... | ... |
@@ -32,7 +32,7 @@ label |
| 32 | 32 |
display: block; |
| 33 | 33 |
font-size: 1.0em; |
| 34 | 34 |
font-weight: bold; |
| 35 |
- text-transform: capitalize; |
|
| 35 |
+ // text-transform: capitalize; |
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
.field |
| ... | ... |
@@ -99,25 +99,62 @@ label |
| 99 | 99 |
} |
| 100 | 100 |
} |
| 101 | 101 |
|
| 102 |
+body |
|
| 103 |
+{
|
|
| 104 |
+ & |
|
| 105 |
+ {
|
|
| 106 |
+ & #connect {display: none;}
|
|
| 107 |
+ & #wait {display: none;}
|
|
| 108 |
+ & #main {display: none;}
|
|
| 109 |
+ } |
|
| 110 |
+ |
|
| 111 |
+ &.offline |
|
| 112 |
+ {
|
|
| 113 |
+ & #connect {display: initial !important;}
|
|
| 114 |
+ } |
|
| 115 |
+ |
|
| 116 |
+ &.connecting |
|
| 117 |
+ {
|
|
| 118 |
+ & #wait {display: initial !important;}
|
|
| 119 |
+ } |
|
| 120 |
+ |
|
| 121 |
+ &.online |
|
| 122 |
+ {
|
|
| 123 |
+ & #main {display: initial !important;}
|
|
| 124 |
+ } |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 102 | 127 |
#head |
| 103 | 128 |
{
|
| 129 |
+ display: flex; |
|
| 130 |
+ flex-direction: row; |
|
| 131 |
+ flex-wrap: wrap; |
|
| 132 |
+ |
|
| 133 |
+ & > #head_left |
|
| 134 |
+ {
|
|
| 135 |
+ flex: 1; |
|
| 136 |
+ text-align: left; |
|
| 137 |
+ } |
|
| 138 |
+ |
|
| 139 |
+ & > #head_right |
|
| 140 |
+ {
|
|
| 141 |
+ flex: 1; |
|
| 104 | 142 |
text-align: right; |
| 105 | 143 |
} |
| 144 |
+} |
|
| 145 |
+ |
|
| 106 | 146 |
|
| 107 | 147 |
#middle |
| 108 | 148 |
{
|
| 109 | 149 |
display: flex; |
| 110 | 150 |
flex-direction: row; |
| 111 | 151 |
|
| 112 |
- & #history {flex: 4;}
|
|
| 152 |
+ & #events {flex: 4;}
|
|
| 113 | 153 |
& #users {flex: 1;}
|
| 114 | 154 |
} |
| 115 | 155 |
|
| 116 | 156 |
#message |
| 117 | 157 |
{
|
| 118 |
- // height: 40px; |
|
| 119 |
- width: 80%; |
|
| 120 |
- |
|
| 121 | 158 |
border: none; |
| 122 | 159 |
|
| 123 | 160 |
background-color: hsl(@hue, 0%, 25%); |
| ... | ... |
@@ -127,34 +164,13 @@ label |
| 127 | 164 |
margin: 4px; |
| 128 | 165 |
} |
| 129 | 166 |
|
| 130 |
-body |
|
| 131 |
-{
|
|
| 132 |
- &:not(.offline):not(.checking):not(.online) |
|
| 133 |
- {
|
|
| 134 |
- & #connect {display: none;}
|
|
| 135 |
- & #wait {display: none;}
|
|
| 136 |
- & #main {display: none;}
|
|
| 137 |
- } |
|
| 138 |
- |
|
| 139 |
- &.offline |
|
| 167 |
+#main > form |
|
| 140 | 168 |
{
|
| 141 |
- & #connect {}
|
|
| 142 |
- & #wait {display: none;}
|
|
| 143 |
- & #main {display: none;}
|
|
| 144 |
- } |
|
| 145 |
- |
|
| 146 |
- &.checking |
|
| 147 |
- {
|
|
| 148 |
- & #connect {display: none;}
|
|
| 149 |
- & #wait {}
|
|
| 150 |
- & #main {display: none;}
|
|
| 151 |
- } |
|
| 169 |
+ display: flex; |
|
| 170 |
+ flex-direction: row; |
|
| 171 |
+ flex-wrap: wrap; |
|
| 152 | 172 |
|
| 153 |
- &.online |
|
| 154 |
- {
|
|
| 155 |
- & #connect {display: none;}
|
|
| 156 |
- & #wait {display: none;}
|
|
| 157 |
- & #main {}
|
|
| 158 |
- } |
|
| 173 |
+ & > #message {flex: 7;}
|
|
| 174 |
+ & > #send {flex: 1;}
|
|
| 159 | 175 |
} |
| 160 | 176 |
|
| 161 | 177 |