@mideind/netskrafl-react 1.0.0-beta.2 → 1.0.0-beta.3

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mideind/netskrafl-react",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.3",
4
4
  "description": "React UI for Netskrafl",
5
5
  "repository": {
6
6
  "type": "git",
@@ -85,15 +85,17 @@ const NetskraflImpl: React.FC<INetskraflProps> = ({ state, tokenExpired }) => {
85
85
 
86
86
  useEffect(() => {
87
87
  // Load the Netskrafl (Mithril) UI for a new user
88
+ // console.log("Mounting Netskrafl UI for user", userEmail);
88
89
  if (!userEmail) return;
89
90
  const container = ref.current;
90
91
  if (!container) {
91
- // console.error("Unable to mount Netskrafl UI");
92
+ console.error("No container for Netskrafl UI");
92
93
  return;
93
94
  }
94
95
  const elemId = `netskrafl-user-${userEmail}`;
95
96
  if (container.firstElementChild?.id === elemId) {
96
97
  // Already exists and correctly mounted
98
+ // console.log("Netskrafl UI already mounted with elemId", elemId);
97
99
  return;
98
100
  }
99
101
  try {
@@ -107,12 +109,13 @@ const NetskraflImpl: React.FC<INetskraflProps> = ({ state, tokenExpired }) => {
107
109
  }
108
110
  });
109
111
  } catch (err) {
110
- // console.error("Failed to mount Netskrafl UI for user", userEmail);
112
+ console.error("Failed to mount Netskrafl UI for user", userEmail);
111
113
  const container = document.getElementById("netskrafl-container");
112
114
  if (container)
113
115
  container.innerHTML = "";
114
116
  }
115
117
  return () => {
118
+ // console.log("Dismounting Netskrafl UI for user", userEmail);
116
119
  // Move the Netskrafl UI to a hidden div under the body element
117
120
  // when the component is unmounted
118
121
  const container = document.getElementById("netskrafl-container");
@@ -1489,7 +1489,7 @@ div.modal-dialog#spinner-dialog {
1489
1489
  }
1490
1490
  div.animated-spinner {
1491
1491
  position: absolute;
1492
- left: 141.5px;
1492
+ left: 50%;
1493
1493
  top: 271.5px;
1494
1494
  }
1495
1495
  svg circle.shadow {
@@ -1841,25 +1841,18 @@ div.submitmove#move-mobile span.glyphicon {
1841
1841
  margin-top: 2px;
1842
1842
  }
1843
1843
  div.force-resign {
1844
- font-size: 18px;
1845
- font-weight: 700;
1844
+ display: flex;
1845
+ flex-direction: column;
1846
+ align-items: center;
1847
+ justify-content: center;
1848
+ font-weight: normal;
1849
+ font-size: 13px;
1846
1850
  margin: 0;
1847
1851
  padding: 6px;
1848
- padding-top: 9px;
1849
- border-radius: 5px;
1850
1852
  cursor: pointer;
1851
- width: 32px;
1852
1853
  height: 26px;
1853
- background-color: var(--ok-button);
1854
- display: block;
1855
- float: right;
1856
- padding-top: 3px;
1857
- padding-bottom: 12px;
1854
+ width: 122px;
1858
1855
  margin-right: 1px;
1859
- width: 96px;
1860
- text-align: center;
1861
- font-weight: normal;
1862
- font-size: 13px;
1863
1856
  color: white;
1864
1857
  background-color: var(--cancel-button);
1865
1858
  }
@@ -2920,7 +2913,7 @@ div.at-top-left div.tilecount div.oc {
2920
2913
  padding-bottom: 2px;
2921
2914
  background-color: transparent;
2922
2915
  }
2923
- div.chat {
2916
+ div.chat-container {
2924
2917
  position: absolute;
2925
2918
  top: 0px;
2926
2919
  left: 0px;
@@ -3048,7 +3041,7 @@ div.gamestats {
3048
3041
  width: 724px;
3049
3042
  height: 580px;
3050
3043
  left: 150px;
3051
- top: 64px;
3044
+ top: 24px;
3052
3045
  padding: 12px;
3053
3046
  border-radius: 5px;
3054
3047
  box-shadow: 3px 3px 5px 2px var(--dark-shadow);
@@ -3057,21 +3050,20 @@ div.statscol {
3057
3050
  display: block;
3058
3051
  float: left;
3059
3052
  width: 50%;
3060
- padding-top: 24px;
3061
3053
  }
3062
3054
  div.statscol p {
3063
- margin-top: 0.85em;
3064
- margin-bottom: 0.8em;
3055
+ margin-top: 0;
3056
+ margin-bottom: 12px;
3065
3057
  }
3066
3058
  div.gamestats p span {
3067
3059
  font-weight: 700;
3068
3060
  }
3069
- div #gamestarted {
3061
+ div#gamestarted {
3070
3062
  width: 100%;
3071
3063
  text-align: center;
3072
3064
  position: relative;
3073
- top: 42px;
3074
- margin-bottom: 24px;
3065
+ margin-top: 16px;
3066
+ margin-bottom: 16px;
3075
3067
  }
3076
3068
  #gamestarted p {
3077
3069
  font-style: italic;
@@ -3657,6 +3649,7 @@ div.listitem {
3657
3649
  top: 0;
3658
3650
  }
3659
3651
  .listitem a {
3652
+ display: flex;
3660
3653
  cursor: pointer;
3661
3654
  }
3662
3655
  .listitem a:link,
@@ -4157,7 +4150,7 @@ h1.usr-info-icon span.glyphicon-coffee-cup {
4157
4150
  div.usr-info-fav {
4158
4151
  display: inline-block;
4159
4152
  position: absolute;
4160
- top: 26px;
4153
+ top: 16px;
4161
4154
  right: 20px;
4162
4155
  color: var(--triple-letter-color);
4163
4156
  font-size: 26px;
@@ -4654,7 +4647,7 @@ div.toggler div.option.small {
4654
4647
  div.toggler div.option.x-small {
4655
4648
  width: 30px;
4656
4649
  height: 20px;
4657
- line-height: 20px;
4650
+ line-height: 22px;
4658
4651
  font-size: 15px;
4659
4652
  padding-top: 1px;
4660
4653
  padding-bottom: 3px;
@@ -4938,7 +4931,7 @@ div.signup-header {
4938
4931
  height: 288px;
4939
4932
  /* 8 lines @ 36px each */
4940
4933
  }
4941
- div.chat,
4934
+ div.chat-container,
4942
4935
  div.twoletter {
4943
4936
  height: 474px;
4944
4937
  }
@@ -5038,6 +5031,9 @@ div.signup-header {
5038
5031
  div.rack-left div.recallbtn {
5039
5032
  display: none;
5040
5033
  }
5034
+ div.force-resign {
5035
+ display: none;
5036
+ }
5041
5037
  div.scramblebtn {
5042
5038
  left: 376px;
5043
5039
  top: 158px;
@@ -5148,7 +5144,7 @@ div.signup-header {
5148
5144
  }
5149
5145
  div.games,
5150
5146
  div.twoletter,
5151
- div.chat {
5147
+ div.chat-container {
5152
5148
  display: none;
5153
5149
  }
5154
5150
  div.right-area {
@@ -5343,12 +5339,12 @@ div.signup-header {
5343
5339
  }
5344
5340
  div.games,
5345
5341
  div.twoletter,
5346
- div.chat {
5342
+ div.chat-container {
5347
5343
  display: block;
5348
5344
  border-radius: 0;
5349
5345
  }
5350
5346
  div.twoletter,
5351
- div.chat {
5347
+ div.chat-container {
5352
5348
  height: 420px;
5353
5349
  }
5354
5350
  div.board-area {
@@ -5990,7 +5986,7 @@ div.signup-header {
5990
5986
  height: 380px;
5991
5987
  }
5992
5988
  div.right-area.with-clock div.twoletter,
5993
- div.right-area.with-clock div.chat {
5989
+ div.right-area.with-clock div.chat-container {
5994
5990
  /* Timed game */
5995
5991
  height: 380px;
5996
5992
  }
@@ -147,8 +147,7 @@ export const ReviewTileSquare: ComponentFunc<ITileSquareAttributes> = (initialVn
147
147
  let cls = game.squareClass(coord) || "";
148
148
  if (cls)
149
149
  cls = "." + cls;
150
- if (vnode.attrs.opponent)
151
- cls += ".opp";
150
+ cls += vnode.attrs.opponent ? ".opp" : ".local";
152
151
  return m("td" + cls, { id: "sq_" + coord }, vnode.children);
153
152
  }
154
153
  };
@@ -287,44 +286,48 @@ export const Board: ComponentFunc<IBoardAttributes> = (initialVnode) => {
287
286
  };
288
287
  }
289
288
 
290
- export const Rack: ComponentFunc<IBoardAttributes> = (initialVnode) => {
289
+ export const Rack: ComponentFunc<IBoardAttributes> = () => {
291
290
  // A rack of 7 tiles
292
- const { view, review } = initialVnode.attrs;
293
- const model = view.model;
294
291
  return {
295
- view: () => {
296
- const game = model.game;
297
- if (!game) return undefined;
298
- let r: VnodeChildren = [];
292
+ view: (vnode) => {
293
+ const { view, review } = vnode.attrs;
299
294
  // If review==true, this is a review rack
300
295
  // that is not a drop target and whose color reflects the
301
296
  // currently shown move.
302
- // If opponent==true, we're showing the opponent's rack
297
+ const model = view.model;
298
+ const game = model.game;
299
+ if (!game) return undefined;
300
+ let r: VnodeChildren = [];
303
301
  const reviewMove = model.reviewMove ?? 0;
302
+ // Avoid flicker when paging through game review screen,
303
+ // as model.reviewMove becomes undefined or 0 while we
304
+ // are fetching the next move to display
305
+ const waiting = review && (reviewMove === 0);
306
+ // If opponent==true, we're showing the opponent's rack
304
307
  const opponent = review && (reviewMove > 0) && (reviewMove % 2 === game.player);
305
308
  for (let i = 1; i <= RACK_SIZE; i++) {
306
- const coord = 'R' + i.toString();
307
- if (game && (coord in game.tiles)) {
309
+ const coord = `R${i}`;
310
+ if (!waiting && game && (coord in game.tiles)) {
308
311
  // We have a tile in this rack slot, but it is a drop target anyway
309
312
  if (review) {
310
313
  r.push(
311
- m(ReviewTileSquare, { view, coord: coord, opponent: opponent },
312
- m(Tile, { view, coord: coord, opponent: opponent })
314
+ m(ReviewTileSquare, { key: coord, view, coord, opponent },
315
+ m(Tile, { view, coord, opponent })
313
316
  )
314
317
  );
315
318
  } else {
316
319
  r.push(
317
- m(DropTargetSquare, { view, coord: coord },
318
- m(Tile, { view, coord: coord, opponent: false })
320
+ m(DropTargetSquare, { key: coord, view, coord },
321
+ m(Tile, { view, coord, opponent: false })
319
322
  )
320
323
  );
321
324
  }
322
325
  }
323
326
  else if (review) {
324
- r.push(m(ReviewTileSquare, { view, coord: coord, opponent: false }));
327
+ r.push(m(ReviewTileSquare, { key: coord, view, coord, opponent: false }));
325
328
  }
326
329
  else {
327
- r.push(m(DropTargetSquare, { view, coord: coord }));
330
+ r.push(m(DropTargetSquare, { key: coord, view, coord }));
328
331
  }
329
332
  }
330
333
  return m(".rack-row", [
@@ -181,7 +181,7 @@ export const Chat: ComponentFunc<IAttributes> = (initialVnode) => {
181
181
  const numMessages = game?.messages ? game.messages.length : 0;
182
182
 
183
183
  return {
184
- view: () => m(".chat",
184
+ view: () => m(".chat-container",
185
185
  {
186
186
  style: "z-index: 6" // Appear on top of board on mobile
187
187
  // key: uuid
@@ -253,7 +253,6 @@ export class View implements IView {
253
253
  );
254
254
  break;
255
255
  default:
256
- // console.log("Unknown route name: " + model.routeName);
257
256
  return [ m("div", t("Þessi vefslóð er ekki rétt")) ];
258
257
  }
259
258
  // Push any open dialogs
@@ -26,7 +26,7 @@ interface IAttributes {
26
26
 
27
27
  export const Tile: ComponentFunc<IAttributes> = (initialVnode) => {
28
28
  // Display a tile on the board or in the rack
29
- const { view, coord, opponent } = initialVnode.attrs;
29
+ const { view, coord } = initialVnode.attrs;
30
30
  const model = view.model;
31
31
 
32
32
  const dragHandler = (ev: MithrilMouseEvent | MithrilTouchEvent) => {
@@ -62,7 +62,8 @@ export const Tile: ComponentFunc<IAttributes> = (initialVnode) => {
62
62
  };
63
63
 
64
64
  return {
65
- view: () => {
65
+ view: (vnode) => {
66
+ const { opponent } = vnode.attrs;
66
67
  const game = model.game;
67
68
  if (!game) return undefined;
68
69
  const isRackTile = coord[0] === 'R';
@@ -94,8 +95,7 @@ export const Tile: ComponentFunc<IAttributes> = (initialVnode) => {
94
95
  ev.preventDefault();
95
96
  };
96
97
  }
97
- }
98
- if (t.freshtile) {
98
+ } else if (t.freshtile) {
99
99
  // A fresh tile on the board that has
100
100
  // just been played by the opponent
101
101
  classes.push("freshtile");