@mideind/netskrafl-react 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/.eslintignore +8 -0
  2. package/.eslintrc.json +13 -0
  3. package/README.md +63 -0
  4. package/dist/cjs/index.css +6837 -0
  5. package/dist/cjs/index.js +3046 -0
  6. package/dist/cjs/index.js.map +1 -0
  7. package/dist/esm/index.css +6837 -0
  8. package/dist/esm/index.js +3046 -0
  9. package/dist/esm/index.js.map +1 -0
  10. package/dist/types.d.ts +41 -0
  11. package/package.json +63 -0
  12. package/rollup.config.js +60 -0
  13. package/src/components/index.ts +2 -0
  14. package/src/components/netskrafl/Netskrafl.stories.tsx +66 -0
  15. package/src/components/netskrafl/Netskrafl.tsx +135 -0
  16. package/src/components/netskrafl/Netskrafl.types.ts +7 -0
  17. package/src/components/netskrafl/index.ts +2 -0
  18. package/src/css/fonts.css +4 -0
  19. package/src/css/glyphs.css +224 -0
  20. package/src/css/skrafl-explo.css +6616 -0
  21. package/src/fonts/glyphicons-regular.eot +0 -0
  22. package/src/fonts/glyphicons-regular.ttf +0 -0
  23. package/src/fonts/glyphicons-regular.woff +0 -0
  24. package/src/index.ts +2 -0
  25. package/src/messages/messages.json +1576 -0
  26. package/src/mithril/actions.ts +319 -0
  27. package/src/mithril/bag.ts +65 -0
  28. package/src/mithril/bestdisplay.ts +74 -0
  29. package/src/mithril/blankdialog.ts +94 -0
  30. package/src/mithril/board.ts +336 -0
  31. package/src/mithril/buttons.ts +303 -0
  32. package/src/mithril/challengedialog.ts +186 -0
  33. package/src/mithril/channel.ts +162 -0
  34. package/src/mithril/chat.ts +228 -0
  35. package/src/mithril/components.ts +496 -0
  36. package/src/mithril/dragdrop.ts +219 -0
  37. package/src/mithril/elopage.ts +180 -0
  38. package/src/mithril/friend.ts +227 -0
  39. package/src/mithril/game.ts +1378 -0
  40. package/src/mithril/gameview.ts +111 -0
  41. package/src/mithril/globalstate.ts +33 -0
  42. package/src/mithril/i18n.ts +186 -0
  43. package/src/mithril/localstorage.ts +133 -0
  44. package/src/mithril/login.ts +122 -0
  45. package/src/mithril/logo.ts +270 -0
  46. package/src/mithril/main.ts +737 -0
  47. package/src/mithril/mithril.ts +29 -0
  48. package/src/mithril/model.ts +817 -0
  49. package/src/mithril/movelistitem.ts +226 -0
  50. package/src/mithril/page.ts +852 -0
  51. package/src/mithril/playername.ts +91 -0
  52. package/src/mithril/promodialog.ts +82 -0
  53. package/src/mithril/recentlist.ts +148 -0
  54. package/src/mithril/request.ts +52 -0
  55. package/src/mithril/review.ts +634 -0
  56. package/src/mithril/rightcolumn.ts +398 -0
  57. package/src/mithril/searchbutton.ts +118 -0
  58. package/src/mithril/statsdisplay.ts +109 -0
  59. package/src/mithril/tabs.ts +169 -0
  60. package/src/mithril/tile.ts +145 -0
  61. package/src/mithril/twoletter.ts +76 -0
  62. package/src/mithril/types.ts +379 -0
  63. package/src/mithril/userinfodialog.ts +171 -0
  64. package/src/mithril/util.ts +304 -0
  65. package/src/mithril/wait.ts +246 -0
  66. package/src/mithril/wordcheck.ts +102 -0
  67. package/tsconfig.json +28 -0
  68. package/vite.config.ts +12 -0
@@ -0,0 +1,737 @@
1
+ /*
2
+
3
+ Main.ts
4
+
5
+ Main screen component
6
+
7
+ Copyright (C) 2024 Miðeind ehf.
8
+ Author: Vilhjalmur Thorsteinsson
9
+
10
+ The Creative Commons Attribution-NonCommercial 4.0
11
+ International Public License (CC-BY-NC 4.0) applies to this software.
12
+ For further information, see https://github.com/mideind/Netskrafl
13
+
14
+ */
15
+
16
+ import { ChallengeAction, ChallengeListItem, IView, UserListItem } from "./types";
17
+ import { VnodeChildren, ComponentFunc, m } from "./mithril";
18
+ import { gameUrl, glyph, glyphGrayed, nbsp } from "./util";
19
+ import { ExploLogoOnly } from "./logo";
20
+ import { mt, t, ts } from "./i18n";
21
+ import { ChallengeButton, Info, LeftLogo, TogglerReady, TogglerReadyTimed, UserId, UserInfoButton } from "./components";
22
+ import { logEvent } from "./channel";
23
+ import { RecentList } from "./recentlist";
24
+ import { EloPage } from "./elopage";
25
+ import { StatsDisplay } from "./statsdisplay";
26
+ import { SearchButton } from "./searchbutton";
27
+ import { makeTabs, updateSelection } from "./tabs";
28
+ import { BestDisplay } from "./bestdisplay";
29
+
30
+ interface IAttributes {
31
+ view: IView;
32
+ }
33
+
34
+ export const Main: ComponentFunc<IAttributes> = () => {
35
+ // Main screen with tabs
36
+
37
+ function vwMainTabs(view: IView): VnodeChildren {
38
+
39
+ const model = view.model;
40
+
41
+ function vwMainTabHeader(): VnodeChildren {
42
+ const numGames = model.numGames;
43
+ const numChallenges = model.numChallenges;
44
+ return [
45
+ m(".header-logo",
46
+ m(m.route.Link,
47
+ {
48
+ href: "/page",
49
+ class: "backlink"
50
+ },
51
+ m(ExploLogoOnly)
52
+ )
53
+ ),
54
+ m("ul", [
55
+ m("li",
56
+ m("a[href='#tabs-1']", [
57
+ glyph("th"), m("span.tab-legend", t("Viðureignir")),
58
+ m("span",
59
+ {
60
+ id: 'numgames',
61
+ style: numGames ? 'display: inline-block' : ''
62
+ },
63
+ numGames
64
+ )
65
+ ])
66
+ ),
67
+ m("li",
68
+ m("a[href='#tabs-2']", [
69
+ glyph("hand-right"), m("span.tab-legend", t("Áskoranir")),
70
+ // Blink if we have timed games where the opponent is ready
71
+ m("span" + (model.oppReady ? ".opp-ready" : ""),
72
+ {
73
+ id: "numchallenges",
74
+ style: numChallenges ? 'display: inline-block' : ''
75
+ },
76
+ numChallenges
77
+ )
78
+ ])
79
+ ),
80
+ m("li",
81
+ m("a[href='#tabs-3']",
82
+ [glyph("user"), m("span.tab-legend", t("Andstæðingar"))]
83
+ )
84
+ ),
85
+ m("li.no-mobile-list",
86
+ m("a[href='#tabs-4']",
87
+ [glyph("bookmark"), m("span.tab-legend", t("Ferill"))]
88
+ )
89
+ )
90
+ ])
91
+ ];
92
+ }
93
+
94
+ function vwGamelist(): VnodeChildren {
95
+
96
+ function vwList(): VnodeChildren {
97
+
98
+ function viewGameList(): VnodeChildren {
99
+
100
+ if (!model.gameList)
101
+ return "";
102
+ return model.gameList.map((item, i: number) => {
103
+
104
+ // Show a list item about a game in progress (or recently finished)
105
+
106
+ function vwOpp(): VnodeChildren {
107
+ let arg = item.oppid === null ? [glyph("cog"), nbsp(), item.opp] : item.opp;
108
+ return m("span.list-opp", { title: item.fullname }, arg);
109
+ }
110
+
111
+ function vwTurn(): VnodeChildren {
112
+ let turnText = "";
113
+ let flagClass = "";
114
+ if (item.my_turn) {
115
+ turnText = ts("Þú átt leik");
116
+ }
117
+ else
118
+ if (item.zombie) {
119
+ turnText = ts("Viðureign lokið");
120
+ flagClass = ".zombie";
121
+ }
122
+ else {
123
+ // {opponent}'s move
124
+ turnText = ts("opp_move", { opponent: item.opp });
125
+ flagClass = ".grayed";
126
+ }
127
+ return m("span.list-myturn",
128
+ m("span.glyphicon.glyphicon-flag" + flagClass, { title: turnText })
129
+ );
130
+ }
131
+
132
+ function vwOverdue(): VnodeChildren {
133
+ if (item.overdue)
134
+ return glyph("hourglass",
135
+ { title: item.my_turn ? "Er að renna út á tíma" : "Getur þvingað fram uppgjöf" }
136
+ );
137
+ return glyphGrayed("hourglass");
138
+ }
139
+
140
+ function vwTileCount(): VnodeChildren {
141
+ const winLose = item.sc0 < item.sc1 ? ".losing" : "";
142
+ return m(".tilecount",
143
+ m(".tc" + winLose, { style: { width: item.tile_count.toString() + "%" } })
144
+ );
145
+ }
146
+
147
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"),
148
+ [
149
+ m(m.route.Link,
150
+ { href: gameUrl(item.url) },
151
+ [
152
+ vwTurn(),
153
+ m("span.list-overdue", vwOverdue()),
154
+ m("span.list-ts-short", item.ts),
155
+ vwOpp()
156
+ ]
157
+ ),
158
+ m(UserInfoButton, {
159
+ view,
160
+ item: { userid: item.oppid, nick: item.opp, fullname: item.fullname },
161
+ }),
162
+ m("span.list-s0", item.sc0),
163
+ m("span.list-colon", ":"),
164
+ m("span.list-s1", item.sc1),
165
+ m("span.list-tc", vwTileCount()),
166
+ m("span.list-manual",
167
+ item.manual
168
+ ? glyph("lightbulb", { title: ts("Keppnishamur") })
169
+ : glyphGrayed("lightbulb")
170
+ )
171
+ ]
172
+ );
173
+ });
174
+ }
175
+
176
+ if (model.gameList === null)
177
+ model.loadGameList();
178
+ return m("div", { id: 'gamelist' }, viewGameList());
179
+ }
180
+
181
+ function vwHint(): VnodeChildren {
182
+ // Show some help if the user has no games in progress
183
+ if (model.loadingGameList || model.gameList === undefined ||
184
+ (model.gameList !== null && model.gameList.length > 0))
185
+ // Either we have games in progress or the game list is being loaded
186
+ return "";
187
+ return m(".hint", { style: { display: "block" } },
188
+ [
189
+ m("p",
190
+ [
191
+ "Ef þig vantar einhvern til að skrafla við, veldu flipann ",
192
+ m(m.route.Link,
193
+ { href: "/main?tab=2" },
194
+ [glyph("user"), nbsp(), "Andstæðingar"]
195
+ ),
196
+ " og skoraðu á tölvuþjarka - ",
197
+ glyph("cog"), nbsp(), m("b", "Amlóða"),
198
+ ", ",
199
+ glyph("cog"), nbsp(), m("b", "Miðlung"),
200
+ " eða ",
201
+ glyph("cog"), nbsp(), m("b", "Fullsterkan"),
202
+ " - eða veldu þér annan leikmann úr stafrófs\u00ADlistunum " + // Soft hyphen
203
+ " sem þar er að finna til að skora á."
204
+ ]
205
+ ),
206
+ m("p",
207
+ [
208
+ "Þú stofnar áskorun með því að smella á bendi-teiknið ",
209
+ glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
210
+ " vinstra megin við nafn andstæðingsins."
211
+ ]
212
+ ),
213
+ m("p",
214
+ "Tölvuþjarkarnir eru ætíð reiðubúnir að skrafla og viðureign við þá " +
215
+ " hefst strax. Aðrir leikmenn þurfa að samþykkja áskorun áður en viðureign hefst."
216
+ ),
217
+ m("p.no-mobile-block",
218
+ [
219
+ m(m.route.Link, { href: "/help" }, "Hjálp"),
220
+ " má fá með því að smella á bláa ",
221
+ glyph("info-sign"), nbsp(), "-", nbsp(),
222
+ "teiknið hér til vinstri."
223
+ ]
224
+ ),
225
+ m("p.no-mobile-block",
226
+ "Þú kemst alltaf aftur í þessa aðalsíðu með því að smella á " +
227
+ "örvarmerkið efst vinstra megin við skraflborðið."
228
+ )
229
+ ]
230
+ );
231
+ }
232
+
233
+ return [
234
+ m(".listitem.listheader",
235
+ [
236
+ m("span.list-myturn", glyphGrayed("flag", { title: ts('Átt þú leik?') })),
237
+ m("span.list-overdue",
238
+ glyphGrayed("hourglass", { title: ts('Langt frá síðasta leik?') })
239
+ ),
240
+ mt("span.list-ts-short", "Síðasti leikur"),
241
+ mt("span.list-opp", "Andstæðingur"),
242
+ mt("span.list-info-hdr", "Ferill"),
243
+ mt("span.list-scorehdr", "Staða"),
244
+ mt("span.list-tc", "Framvinda"),
245
+ m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
246
+ ]
247
+ ),
248
+ vwList(),
249
+ vwHint()
250
+ ];
251
+ }
252
+
253
+ function vwChallenges(showReceived: boolean): VnodeChildren {
254
+
255
+ function vwList(): VnodeChildren {
256
+
257
+ if (!model.state) return undefined;
258
+ const state = model.state;
259
+
260
+ function itemize(item: ChallengeListItem, i: number) {
261
+
262
+ // Generate a list item about a pending challenge (issued or received)
263
+
264
+ function challengeDescription(json: { duration?: number; }) {
265
+ /* Return a human-readable string describing a challenge
266
+ according to the enclosed preferences */
267
+ if (!json || json.duration === undefined || json.duration === 0)
268
+ /* Normal unbounded (untimed) game */
269
+ return t("Venjuleg ótímabundin viðureign");
270
+ return t("with_clock", { duration: json.duration.toString() });
271
+ }
272
+
273
+ function markChallenge(ev: Event) {
274
+ // Clicked the icon at the beginning of the line,
275
+ // to decline a received challenge or retract an issued challenge
276
+ const action: ChallengeAction = item.received ? "decline" : "retract";
277
+ const param = { destuser: item.userid, action: action, key: item.key };
278
+ model.modifyChallenge(param);
279
+ ev.preventDefault();
280
+ }
281
+
282
+ function clickChallenge(ev: Event) {
283
+ // Clicked the hotspot area to accept a received challenge
284
+ ev.preventDefault();
285
+ if (!model.moreGamesAllowed()) {
286
+ // User must be a friend to be able to accept more challenges
287
+ logEvent("hit_game_limit",
288
+ {
289
+ userid: state.userId,
290
+ locale: state.locale,
291
+ limit: model.maxFreeGames
292
+ }
293
+ );
294
+ // Promote a subscription to Netskrafl/Explo
295
+ view.showFriendPromo();
296
+ return;
297
+ }
298
+ if (item.received) {
299
+ if (item.prefs && item.prefs.duration !== undefined && item.prefs.duration > 0)
300
+ // Timed game: display a modal wait dialog
301
+ view.pushDialog("wait", {
302
+ oppId: item.userid,
303
+ oppNick: item.opp,
304
+ oppName: item.fullname,
305
+ duration: item.prefs.duration,
306
+ challengeKey: item.key,
307
+ });
308
+ else
309
+ // Ask the server to create a new game and route to it
310
+ model.newGame(item.userid, false);
311
+ }
312
+ else {
313
+ // Clicking on a sent challenge, i.e. a timed game
314
+ // where the opponent is waiting and ready to start
315
+ view.showAcceptDialog(item.userid, item.opp, item.key);
316
+ }
317
+ }
318
+
319
+ const oppReady = !item.received && item.opp_ready &&
320
+ item.prefs && item.prefs.duration !== undefined &&
321
+ item.prefs.duration > 0;
322
+ const clickable = item.received || oppReady;
323
+ const descr = challengeDescription(item.prefs);
324
+
325
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"),
326
+ [
327
+ m("span.list-ch",
328
+ { onclick: markChallenge },
329
+ item.received ?
330
+ glyph("thumbs-down", { title: ts("Hafna") })
331
+ :
332
+ glyph("hand-right", { title: ts("Afturkalla") })
333
+ ),
334
+ m(clickable ? "a" : "span",
335
+ clickable ? {
336
+ href: "#",
337
+ onclick: clickChallenge,
338
+ class: oppReady ? "opp-ready" : ""
339
+ } : {},
340
+ [
341
+ m("span.list-ts", item.ts),
342
+ m("span.list-nick", { title: item.fullname }, item.opp),
343
+ m("span.list-chall",
344
+ [
345
+ item.prefs.fairplay ? m("span.fairplay-btn", { title: ts("Án hjálpartækja") }) : "",
346
+ item.prefs.manual ? m("span.manual-btn", { title: ts("Keppnishamur") }) : "",
347
+ descr
348
+ ]
349
+ )
350
+ ]
351
+ ),
352
+ m(UserInfoButton,
353
+ {
354
+ view,
355
+ item: {
356
+ userid: item.userid,
357
+ nick: item.opp,
358
+ fullname: item.fullname,
359
+ }
360
+ }
361
+ ),
362
+ ]
363
+ );
364
+ }
365
+
366
+ let cList: ChallengeListItem[] = [];
367
+ if (model.challengeList)
368
+ cList = showReceived ?
369
+ model.challengeList.filter((item) => item.received) :
370
+ model.challengeList.filter((item) => !item.received);
371
+
372
+ return m("div",
373
+ {
374
+ id: showReceived ? 'chall-received' : 'chall-sent',
375
+ oninit: () => {
376
+ if (model.challengeList === null)
377
+ model.loadChallengeList();
378
+ }
379
+ },
380
+ cList.map(itemize)
381
+ );
382
+ }
383
+
384
+ const n = vwList();
385
+ if (!n) return [];
386
+
387
+ if (showReceived)
388
+ // Challenges received
389
+ return [
390
+ m(".listitem.listheader",
391
+ [
392
+ m("span.list-icon", glyphGrayed("thumbs-down", { title: ts('Hafna') })),
393
+ mt("span.list-ts", "Hvenær"),
394
+ mt("span.list-nick", "Áskorandi"),
395
+ mt("span.list-chall", "Hvernig"),
396
+ mt("span.list-info-hdr", "Ferill"),
397
+ ]
398
+ ),
399
+ n
400
+ ];
401
+ else
402
+ // Challenges sent
403
+ return [
404
+ m(".listitem.listheader",
405
+ [
406
+ m("span.list-icon", glyphGrayed("hand-right", { title: ts('Afturkalla') })),
407
+ mt("span.list-ts", "Hvenær"),
408
+ mt("span.list-nick", "Andstæðingur"),
409
+ mt("span.list-chall", "Hvernig"),
410
+ mt("span.list-info-hdr", "Ferill"),
411
+ ]
412
+ ),
413
+ n
414
+ ];
415
+ }
416
+
417
+ function vwRecentList(): VnodeChildren {
418
+
419
+ function vwList(): VnodeChildren {
420
+ if (model.recentList === null)
421
+ model.loadRecentList();
422
+ return m(RecentList,
423
+ {
424
+ view,
425
+ id: "recentlist",
426
+ recentList: model.recentList || [],
427
+ }
428
+ );
429
+ }
430
+
431
+ return [
432
+ m(".listitem.listheader",
433
+ [
434
+ m("span.list-win", glyphGrayed("bookmark", { title: ts('Sigur') })),
435
+ mt("span.list-ts-short", "Viðureign lauk"),
436
+ mt("span.list-nick", "Andstæðingur"),
437
+ mt("span.list-scorehdr", "Úrslit"),
438
+ m("span.list-elo-hdr",
439
+ [
440
+ m("span.glyphicon.glyphicon-user.elo-hdr-left", { title: ts('Mennskir andstæðingar') }),
441
+ t("Elo"),
442
+ m("span.glyphicon.glyphicon-cog.elo-hdr-right", { title: ts('Allir andstæðingar') })
443
+ ]
444
+ ),
445
+ mt("span.list-duration", "Lengd"),
446
+ m("span.list-manual", glyphGrayed("lightbulb", { title: ts('Keppnishamur') }))
447
+ ]
448
+ ),
449
+ vwList()
450
+ ];
451
+ }
452
+
453
+ function vwUserButton(id: string, icon: string, text: string): VnodeChildren {
454
+ // Select the type of user list (robots, fav, alike, elo)
455
+ const sel = model.userListCriteria ? model.userListCriteria.query : "robots";
456
+ const spec = (id === "elo") ? "human" : "";
457
+ return m("span",
458
+ {
459
+ className: (id === sel ? "shown" : ""),
460
+ id: id,
461
+ onclick: (ev: Event) => {
462
+ model.loadUserList({ query: id, spec: spec }, true);
463
+ ev.preventDefault();
464
+ }
465
+ },
466
+ [glyph(icon, { style: { padding: 0 } }), nbsp(), text]
467
+ );
468
+ }
469
+
470
+ function vwUserList(): VnodeChildren {
471
+
472
+ function vwList(list: UserListItem[]) {
473
+
474
+ function itemize(item: UserListItem, i: number) {
475
+
476
+ // Generate a list item about a user
477
+
478
+ const isRobot = item.userid.indexOf("robot-") === 0;
479
+ let fullname: VnodeChildren = [];
480
+
481
+ // Online and accepting challenges
482
+ if (item.ready && !isRobot) {
483
+ fullname.push(m("span.ready-btn", { title: ts("Álínis og tekur við áskorunum") }));
484
+ fullname.push(nbsp());
485
+ }
486
+ // Willing to accept challenges for timed games
487
+ if (item.ready_timed) {
488
+ fullname.push(m("span.timed-btn", { title: ts("Til í viðureign með klukku") }));
489
+ fullname.push(nbsp());
490
+ }
491
+ // Fair play commitment
492
+ if (item.fairplay) {
493
+ fullname.push(m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }));
494
+ fullname.push(nbsp());
495
+ }
496
+ fullname.push(item.fullname);
497
+
498
+ function fav(): VnodeChildren {
499
+ if (isRobot)
500
+ return m("span.list-fav", { style: { cursor: "default" } }, glyph("star-empty"));
501
+ return m("span.list-fav",
502
+ {
503
+ title: ts("Uppáhald"),
504
+ onclick: (ev: Event) => {
505
+ item.fav = !item.fav;
506
+ model.markFavorite(item.userid, item.fav);
507
+ ev.preventDefault();
508
+ }
509
+ },
510
+ glyph(item.fav ? "star" : "star-empty")
511
+ );
512
+ }
513
+
514
+ function userLink(): VnodeChildren {
515
+ if (isRobot)
516
+ return m("a",
517
+ {
518
+ href: "",
519
+ onclick: (ev: Event) => {
520
+ // Start a new game against the robot
521
+ model.newGame(item.userid, false);
522
+ ev.preventDefault();
523
+ }
524
+ },
525
+ [
526
+ m("span.list-nick", [glyph("cog"), nbsp(), item.nick]),
527
+ m("span.list-fullname-robot", fullname)
528
+ ]
529
+ );
530
+ else
531
+ return m.fragment({}, [
532
+ m("span.list-nick", item.nick),
533
+ m("span.list-fullname", fullname),
534
+ m("span.list-human-elo", item.human_elo)
535
+ ]);
536
+ }
537
+
538
+ return m(".listitem" + (i % 2 === 0 ? ".oddlist" : ".evenlist"),
539
+ [
540
+ m(ChallengeButton, { view, item }),
541
+ fav(),
542
+ userLink(),
543
+ m(UserInfoButton, { view, item }),
544
+ ]
545
+ );
546
+ }
547
+
548
+ return m("div", { id: "userlist" }, list.map(itemize));
549
+ }
550
+
551
+ // The type of list to show; by default it's 'robots'
552
+ const query = model.userListCriteria?.query ?? "";
553
+ const spec = model.userListCriteria?.spec ?? "";
554
+ const listType = query || "robots";
555
+ if (listType === "elo")
556
+ // Show Elo list
557
+ return [ m(EloPage, { id: "elolist", view: view, key: "elolist" }) ];
558
+ // Show normal user list
559
+ let list: UserListItem[] = [];
560
+ if (model.userList === undefined) {
561
+ // We are loading a fresh user list
562
+ /* pass */
563
+ }
564
+ else
565
+ if (model.userList === null || query !== listType)
566
+ model.loadUserList({ query: listType, spec: "" }, true);
567
+ else
568
+ list = model.userList;
569
+ const nothingFound = list.length === 0 && listType === "search" && spec !== "";
570
+ const robotList = listType === "robots";
571
+ return [
572
+ m(".listitem.listheader",
573
+ [
574
+ m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
575
+ m("span.list-fav", glyph("star-empty", { title: ts('Uppáhald') })),
576
+ mt("span.list-nick", "Einkenni"),
577
+ mt("span.list-fullname", "Nafn og merki"),
578
+ robotList ? "" : mt("span.list-human-elo[id='usr-list-elo']", "Elo"),
579
+ robotList ? "" : mt("span.list-info-hdr[id='usr-list-info']", "Ferill"),
580
+ ]
581
+ ),
582
+ vwList(list),
583
+ // Show indicator if search didn't find any users matching the criteria
584
+ nothingFound ?
585
+ m("div",
586
+ { id: "user-no-match", style: { display: "block" } },
587
+ [
588
+ glyph("search"),
589
+ " ",
590
+ m("span", { id: "search-prefix" }, spec),
591
+ t(" finnst ekki")
592
+ ]
593
+ )
594
+ : undefined
595
+ ];
596
+ }
597
+
598
+ function vwStats(): VnodeChildren {
599
+ // View the user's own statistics summary
600
+ const ownStats = model.ownStats;
601
+ if (ownStats === null)
602
+ model.loadOwnStats();
603
+ return ownStats && m(StatsDisplay, { view, id: 'own-stats', ownStats });
604
+ }
605
+
606
+ function vwBest(): VnodeChildren {
607
+ // View the user's own best game and word scores
608
+ const ownStats = model.ownStats;
609
+ if (ownStats === null)
610
+ model.loadOwnStats();
611
+ return ownStats && m(BestDisplay, { id: 'own-best', ownStats, myself: true });
612
+ }
613
+
614
+ function tab1(): VnodeChildren {
615
+ // Ongoing games
616
+ return m("div", { id: 'tabs-1' },
617
+ [
618
+ mt("p.no-mobile-block",
619
+ [
620
+ mt("strong", "Viðureignir sem standa yfir"),
621
+ "click_on_game", // "Click on a game to view it and make a move if"
622
+ glyph("flag"),
623
+ " þú átt leik"
624
+ ]
625
+ ),
626
+ vwGamelist()
627
+ ]
628
+ );
629
+ }
630
+
631
+ function tab2(): VnodeChildren {
632
+ // Challenges received and sent
633
+ return m("div", { id: 'tabs-2' },
634
+ [
635
+ mt("p.no-mobile-block",
636
+ [
637
+ mt("strong", "Skorað á þig"),
638
+ "click_on_challenge", // "Click on a challenge to accept it and start a game, or on"
639
+ glyph("thumbs-down", { style: { "margin-left": "6px", "margin-right": "6px" } }),
640
+ " til að hafna henni"
641
+ ]
642
+ ),
643
+ vwChallenges(true),
644
+ mt("p.no-mobile-block",
645
+ [
646
+ mt("strong", "Þú skorar á aðra"),
647
+ " - smelltu á ",
648
+ glyph("hand-right", { style: { "margin-left": "6px", "margin-right": "6px" } }),
649
+ " til að afturkalla áskorun"
650
+ ]
651
+ ),
652
+ vwChallenges(false)
653
+ ]
654
+ );
655
+ }
656
+
657
+ function tab3(): VnodeChildren {
658
+ // Opponent lists
659
+ return m("div", { id: 'tabs-3' },
660
+ [
661
+ m("div", { id: 'initials' },
662
+ [
663
+ m(".user-cat[id='user-headings']",
664
+ [
665
+ vwUserButton("robots", "cog", ts("Þjarkar")),
666
+ " ",
667
+ vwUserButton("fav", "star", ts("Uppáhalds")),
668
+ " ",
669
+ vwUserButton("live", "flash", ts("Álínis")),
670
+ " ",
671
+ vwUserButton("alike", "resize-small", ts("Svipaðir")),
672
+ " ",
673
+ vwUserButton("elo", "crown", ts("Topp 100"))
674
+ ]
675
+ ),
676
+ m(SearchButton, { view })
677
+ ]
678
+ ),
679
+ vwUserList()
680
+ ]
681
+ );
682
+ }
683
+
684
+ function tab4(): VnodeChildren {
685
+ // Recent games and statistics
686
+ return m("div", { id: 'tabs-4' },
687
+ [
688
+ vwStats(),
689
+ vwBest(),
690
+ mt("p.no-mobile-block",
691
+ [
692
+ mt("strong", "Nýlegar viðureignir þínar"),
693
+ "click_to_review" // "Click on a game to review it"
694
+ ]
695
+ ),
696
+ vwRecentList()
697
+ ]
698
+ );
699
+ }
700
+
701
+ return m(".tabbed-page",
702
+ m("div", { id: 'main-tabs' },
703
+ [
704
+ vwMainTabHeader(),
705
+ m("div.tab-scroll-area", [
706
+ tab1(),
707
+ tab2(),
708
+ tab4(),
709
+ tab3(),
710
+ ]),
711
+ ]
712
+ )
713
+ );
714
+ }
715
+
716
+ return {
717
+ view: (vnode) => {
718
+ const view = vnode.attrs.view;
719
+ return m.fragment({}, [
720
+ m(LeftLogo), // No legend, scale up by 50%
721
+ m(UserId, { view }),
722
+ m(Info),
723
+ m(TogglerReady, { view }),
724
+ m(TogglerReadyTimed, { view }),
725
+ m("div",
726
+ {
727
+ oncreate: (vnode) => {
728
+ makeTabs(view, "main-tabs", undefined, false, vnode);
729
+ },
730
+ onupdate: updateSelection
731
+ },
732
+ vwMainTabs(view),
733
+ ),
734
+ ]);
735
+ }
736
+ };
737
+ };