@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,180 @@
1
+ /*
2
+
3
+ EloPage.ts
4
+
5
+ Elo status list 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 { IView, UserListItem } from "./types";
17
+ import { ComponentFunc, VnodeChildren, m } from "./mithril";
18
+ import { ts, mt } from "./i18n";
19
+ import { glyph, glyphGrayed, nbsp } from "./util";
20
+ import { ChallengeButton, UserInfoButton } from "./components";
21
+
22
+ type EloListSelection = "human" | "all" | "manual";
23
+
24
+ interface IAttributes {
25
+ view: IView;
26
+ id: string;
27
+ key: string;
28
+ }
29
+
30
+ export const EloPage: ComponentFunc<IAttributes> = () => {
31
+
32
+ // Show the header of an Elo ranking list and then the list itself
33
+
34
+ let sel: EloListSelection = "human"; // Default: show ranking for human games only
35
+
36
+ return {
37
+ view: (vnode) => {
38
+ return m.fragment({}, [
39
+ m(".listitem.listheader", { key: "header" },
40
+ [
41
+ m("span.list-ch", glyphGrayed("hand-right", { title: ts('Skora á') })),
42
+ mt("span.list-rank", "Röð"),
43
+ m("span.list-rank-no-mobile", { title: ts('Röð í gær') }, ts("1d")),
44
+ m("span.list-rank-no-mobile", { title: ts('Röð fyrir viku') }, ts("7d")),
45
+ mt("span.list-nick-elo", "Einkenni"),
46
+ m("span.list-elo", { title: ts('Elo-stig') }, ts("Elo")),
47
+ m("span.list-elo-no-mobile", { title: ts('Elo-stig í gær') }, ts("1d")),
48
+ m("span.list-elo-no-mobile", { title: ts('Elo-stig fyrir viku') }, ts("7d")),
49
+ m("span.list-elo-no-mobile", { title: ts('Elo-stig fyrir mánuði') }, ts("30d")),
50
+ m("span.list-games", { title: ts('Fjöldi viðureigna') }, glyph("th")),
51
+ m("span.list-ratio", { title: ts('Vinningshlutfall') }, glyph("bookmark")),
52
+ m("span.list-avgpts", { title: ts('Meðalstigafjöldi') }, glyph("dashboard")),
53
+ mt("span.list-info-hdr", "Ferill"),
54
+ // m("span.list-newbag", glyphGrayed("shopping-bag", { title: ts('Gamli pokinn') })),
55
+ m(".toggler[id='elo-toggler']", { title: ts("elo_list_choice") },
56
+ [
57
+ m(".option.x-small",
58
+ {
59
+ // Show ranking for human games only
60
+ className: (sel === "human" ? "selected" : ""),
61
+ onclick: (ev: Event) => { sel = "human"; ev.preventDefault(); },
62
+ },
63
+ glyph("user")
64
+ ),
65
+ m(".option.x-small",
66
+ {
67
+ // Show ranking for all games, including robots
68
+ className: (sel === "all" ? "selected" : ""),
69
+ onclick: (ev: Event) => { sel = "all"; ev.preventDefault(); },
70
+ },
71
+ glyph("cog")
72
+ ),
73
+ m(".option.x-small",
74
+ {
75
+ // Show ranking for manual games only
76
+ className: (sel === "manual" ? "selected" : ""),
77
+ onclick: (ev: Event) => { sel = "manual"; ev.preventDefault(); },
78
+ },
79
+ glyph("lightbulb")
80
+ )
81
+ ]
82
+ )
83
+ ]
84
+ ),
85
+ m(EloList,
86
+ {
87
+ id: vnode.attrs.id,
88
+ sel: sel,
89
+ view: vnode.attrs.view,
90
+ key: sel,
91
+ }
92
+ )
93
+ ]);
94
+ }
95
+ };
96
+
97
+ };
98
+
99
+ const EloList: ComponentFunc<{
100
+ view: IView;
101
+ id: string;
102
+ sel: EloListSelection;
103
+ }> = (initialVnode) => {
104
+
105
+ const outerView = initialVnode.attrs.view;
106
+ const model = outerView.model;
107
+ const state = model.state;
108
+
109
+ return {
110
+
111
+ view: (vnode) => {
112
+
113
+ function itemize(item: UserListItem, i: number) {
114
+
115
+ // Generate a list item about a user in an Elo ranking table
116
+
117
+ const isRobot = item.userid.indexOf("robot-") === 0;
118
+
119
+ function rankStr(rank: number, ref?: number): string {
120
+ // Return a rank string or dash if no rank or not meaningful
121
+ // (i.e. if the reference, such as the number of games, is zero)
122
+ if (rank === 0 || (ref !== undefined && ref === 0))
123
+ return "--";
124
+ return rank.toString();
125
+ }
126
+
127
+ let nick: VnodeChildren = item.nick;
128
+ let ch = m("span.list-ch", nbsp());
129
+ const userId = state?.userId ?? "";
130
+ if (item.userid != userId && !item.inactive) {
131
+ ch = m(ChallengeButton, { view: outerView, item });
132
+ }
133
+ if (isRobot) {
134
+ nick = m("span", [glyph("cog"), nbsp(), nick]);
135
+ }
136
+ if (item.fairplay && !isRobot)
137
+ nick = m("span",
138
+ [m("span.fairplay-btn", { title: ts("Skraflar án hjálpartækja") }), nick]);
139
+
140
+ return m(".listitem",
141
+ {
142
+ key: vnode.attrs.sel + i,
143
+ className: (i % 2 === 0 ? "oddlist" : "evenlist")
144
+ },
145
+ [
146
+ ch, // Challenge icon (right-hand)
147
+ m("span.list-rank.bold", rankStr(item.rank)),
148
+ m("span.list-rank-no-mobile", rankStr(item.rank_yesterday)),
149
+ m("span.list-rank-no-mobile", rankStr(item.rank_week_ago)),
150
+ m("span.list-nick-elo", { title: item.fullname }, nick),
151
+ m("span.list-elo.bold", item.elo),
152
+ m("span.list-elo-no-mobile", rankStr(item.elo_yesterday, item.games_yesterday)),
153
+ m("span.list-elo-no-mobile", rankStr(item.elo_week_ago, item.games_week_ago)),
154
+ m("span.list-elo-no-mobile", rankStr(item.elo_month_ago, item.games_month_ago)),
155
+ m("span.list-games.bold", item.games >= 100000 ? Math.round(item.games / 1000) + "K" : item.games),
156
+ m("span.list-ratio", item.ratio + "%"),
157
+ m("span.list-avgpts", item.avgpts),
158
+ item.userid === userId ? nbsp() : m(UserInfoButton, { view: outerView, item }),
159
+ ]
160
+ );
161
+ }
162
+
163
+ let list: UserListItem[] = [];
164
+ if (model.userList === undefined) {
165
+ // Loading in progress
166
+ // pass
167
+ }
168
+ else if (model.userList === null || model.userListCriteria?.query !== "elo" ||
169
+ model.userListCriteria.spec !== vnode.attrs.sel.toString()) {
170
+ // We're not showing the correct list: request a new one
171
+ model.loadUserList({ query: "elo", spec: vnode.attrs.sel }, true);
172
+ }
173
+ else {
174
+ list = model.userList;
175
+ }
176
+ return m("div", { id: vnode.attrs.id }, list.map(itemize));
177
+ }
178
+
179
+ };
180
+ };
@@ -0,0 +1,227 @@
1
+ /*
2
+
3
+ Friend.ts
4
+
5
+ Code for dialog components used in management of friends
6
+
7
+ Copyright (C) 2023 Miðeind ehf.
8
+ Original author: Vilhjálmur Þorsteinsson
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
+ export {
17
+ FriendPromoteDialog, FriendThanksDialog,
18
+ FriendCancelDialog, FriendCancelConfirmDialog
19
+ };
20
+
21
+ import { m, ComponentFunc, VnodeDOM } from "./mithril";
22
+ import { IView } from "./types";
23
+ import { DialogButton } from "./components";
24
+ import { glyph, nbsp, buttonOver, buttonOut, registerSalesCloud } from "./util";
25
+
26
+ const FriendPromoteDialog: ComponentFunc<{
27
+ view: IView;
28
+ }> = (initialVnode) => {
29
+
30
+ // A dialog that offers friendship to the user
31
+
32
+ const attrs = initialVnode.attrs;
33
+ const view = attrs.view;
34
+ const model = view.model;
35
+
36
+ // Load the friendHTML if not already loaded
37
+ model.loadFriendPromo();
38
+
39
+ function onUpdate(vnode: VnodeDOM) {
40
+ if (!vnode || !vnode.dom)
41
+ return;
42
+ const noButtons = vnode.dom.getElementsByClassName("btn-promo-no") as HTMLCollectionOf<HTMLElement>;
43
+ if (noButtons) {
44
+ // Override onclick, onmouseover and onmouseout for No buttons
45
+ for (let btn of noButtons) {
46
+ btn.onclick = (ev: MouseEvent) => { view.popDialog(); ev.preventDefault(); };
47
+ btn.onmouseover = buttonOver;
48
+ btn.onmouseout = buttonOut;
49
+ }
50
+ }
51
+ // Override onmouseover and onmouseout for Yes buttons
52
+ const yesButtons = vnode.dom.getElementsByClassName("btn-promo-yes") as HTMLCollectionOf<HTMLElement>;
53
+ if (yesButtons) {
54
+ for (let btn of yesButtons) {
55
+ btn.onmouseover = buttonOver;
56
+ btn.onmouseout = buttonOut;
57
+ }
58
+ // We have a 'yes' button: register SalesCloud to handle it
59
+ registerSalesCloud();
60
+ }
61
+ }
62
+
63
+ return {
64
+ view: () => {
65
+ return m(".modal-dialog",
66
+ { id: "promo-dialog", style: { visibility: "visible" } },
67
+ m(".ui-widget.ui-widget-content.ui-corner-all", { id: "promo-form" },
68
+ m(".promo-content",
69
+ {
70
+ oncreate: (vnode) => onUpdate(vnode),
71
+ onupdate: (vnode) => onUpdate(vnode)
72
+ },
73
+ m.trust(model.friendHTML || "<p>Sæki texta...</p>")
74
+ )
75
+ )
76
+ );
77
+ }
78
+ }
79
+ };
80
+
81
+ const FriendThanksDialog: ComponentFunc<{
82
+ view: IView;
83
+ }> = (initialVnode) => {
84
+
85
+ // A dialog that offers friendship to the user
86
+
87
+ return {
88
+ view: () => {
89
+ return m(".modal-dialog",
90
+ { id: "thanks-dialog", style: { visibility: "visible" } },
91
+ m(".ui-widget.ui-widget-content.ui-corner-all", { id: "thanks-form" },
92
+ [
93
+ m(".thanks-content", [
94
+ m("h3", "Frábært!"),
95
+ m("p", [
96
+ "Bestu þakkir fyrir að gerast ", glyph("coffee-cup"), nbsp(), m("b", "Vinur Netskrafls"), "."
97
+ ]),
98
+ m("p", [
99
+ "Fríðindi þín virkjast um leið og greiðsla hefur verið staðfest. ",
100
+ m("b", "Það getur tekið nokkrar mínútur."),
101
+ " Þú færð staðfestingu og kvittun í tölvupósti."
102
+ ]),
103
+ ]),
104
+ m(DialogButton,
105
+ {
106
+ id: "btn-thanks",
107
+ title: "Áfram",
108
+ onclick: (ev: Event) => {
109
+ // Go back to the main page
110
+ // The dialog is popped automatically upon the route change
111
+ m.route.set("/main");
112
+ ev.preventDefault();
113
+ }
114
+ },
115
+ glyph("ok")
116
+ )
117
+ ]
118
+ )
119
+ );
120
+ }
121
+ }
122
+ };
123
+
124
+ const FriendCancelDialog: ComponentFunc<{
125
+ view: IView;
126
+ }> = (initialVnode) => {
127
+
128
+ // A dialog that offers friendship to the user
129
+
130
+ const attrs = initialVnode.attrs;
131
+ const view = attrs.view;
132
+
133
+ return {
134
+ view: () => {
135
+ return m(".modal-dialog",
136
+ { id: "cancel-dialog", style: { visibility: "visible" } },
137
+ m(".ui-widget.ui-widget-content.ui-corner-all", { id: "cancel-form" }, [
138
+ m("div", { id: "cancel-content" }, [
139
+ m("h3", "Hætta sem vinur Netskrafls?"),
140
+ m("p", [
141
+ "Viltu hætta sem ", glyph("coffee-cup"), nbsp(), m("b", "vinur Netskrafls"),
142
+ " og missa þar með þau fríðindi sem því tengjast?"
143
+ ]),
144
+ m("p", "Fríðindin eru:"),
145
+ m("ul", [
146
+ m("li", [
147
+ m("b", "Ótakmarkaður fjöldi viðureigna"), " í gangi samtímis", m("br"),
148
+ "(í stað 8 að hámarki)"
149
+ ]),
150
+ m("li", [
151
+ "Aðgangur að ", m("b", "yfirliti"), " í lok viðureignar"
152
+ ]),
153
+ m("li", [
154
+ m("b", "Keppnishamur"), " án \"græna þumalsins\""
155
+ ]),
156
+ ]),
157
+ m(DialogButton,
158
+ {
159
+ id: "btn-cancel-no",
160
+ title: "Nei",
161
+ onclick: (ev: Event) => {
162
+ ev.preventDefault();
163
+ view.popDialog();
164
+ }
165
+ },
166
+ [ glyph("remove"), " Nei" ]
167
+ ),
168
+ m(DialogButton,
169
+ {
170
+ id: "btn-cancel-yes",
171
+ title: "Já",
172
+ onclick: (ev: Event) => {
173
+ ev.preventDefault();
174
+ view.popDialog();
175
+ // Initiate cancellation of the friendship
176
+ view.cancelFriendship();
177
+ }
178
+ },
179
+ [ glyph("ok"), " Já, vil hætta" ]
180
+ )
181
+ ]),
182
+ ])
183
+ );
184
+ }
185
+ }
186
+ };
187
+
188
+ const FriendCancelConfirmDialog: ComponentFunc<{
189
+ view: IView;
190
+ }> = (initialVnode) => {
191
+
192
+ // A dialog that confirms cancellation of friendship
193
+
194
+ const attrs = initialVnode.attrs;
195
+ const view = attrs.view;
196
+
197
+ return {
198
+ view: () => {
199
+ return m(".modal-dialog",
200
+ { id: "confirm-dialog", style: { visibility: "visible" } },
201
+ m(".ui-widget.ui-widget-content.ui-corner-all", { id: "confirm-form" },
202
+ [
203
+ m(".confirm-content", [
204
+ m("h3", "Staðfesting"),
205
+ m("p", [
206
+ "Þú ert ekki lengur skráð(ur) sem ",
207
+ glyph("coffee-cup"), nbsp(), m("b", "vinur Netskrafls"), "."
208
+ ]),
209
+ ]),
210
+ m(DialogButton,
211
+ {
212
+ id: "btn-thanks",
213
+ title: "Áfram",
214
+ onclick: (ev: Event) => {
215
+ ev.preventDefault();
216
+ view.popDialog();
217
+ }
218
+ },
219
+ glyph("ok")
220
+ )
221
+ ]
222
+ )
223
+ );
224
+ }
225
+ }
226
+ };
227
+