ssb-chess-mithril 2.0.9 → 2.1.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 (35) hide show
  1. package/css/chessground-examples/README.md +14 -4
  2. package/css/chessground-examples/assets/3d.css +24 -23
  3. package/css/chessground-examples/assets/chessground.css +73 -15
  4. package/css/chessground-examples/assets/examples.css +1 -1
  5. package/css/chessground-examples/assets/theme.css +37 -26
  6. package/css/chessground-examples/index.html +5 -5
  7. package/css/chessground-examples/package-lock.json +1747 -8086
  8. package/css/chessground-examples/package.json +32 -17
  9. package/css/chessground-examples/pnpm-lock.yaml +874 -0
  10. package/css/chessground-examples/rollup.config.mjs +20 -0
  11. package/css/chessground-examples/src/main.ts +81 -48
  12. package/css/chessground-examples/src/units/anim.ts +63 -42
  13. package/css/chessground-examples/src/units/basics.ts +73 -13
  14. package/css/chessground-examples/src/units/fen.ts +21 -18
  15. package/css/chessground-examples/src/units/in3d.ts +26 -26
  16. package/css/chessground-examples/src/units/perf.ts +29 -15
  17. package/css/chessground-examples/src/units/play.ts +71 -52
  18. package/css/chessground-examples/src/units/svg.ts +230 -102
  19. package/css/chessground-examples/src/units/unit.ts +47 -18
  20. package/css/chessground-examples/src/units/viewOnly.ts +11 -11
  21. package/css/chessground-examples/src/units/zh.ts +25 -23
  22. package/css/chessground-examples/src/util.ts +30 -18
  23. package/css/chessground-examples/tsconfig.json +6 -4
  24. package/css/miniboards.css +1 -0
  25. package/index.js +87 -78
  26. package/package.json +4 -4
  27. package/ui/game/PieceGraveyard.js +1 -1
  28. package/ui/game/chat.js +51 -0
  29. package/ui/game/gameHistory.js +5 -1
  30. package/ui/game/gameView.js +242 -269
  31. package/ui/miniboard/miniboard.js +8 -5
  32. package/ui/pageLayout/navigation.js +11 -7
  33. package/css/chessground-examples/gulpfile.js +0 -52
  34. package/css/chessground-examples/index.standalone.html +0 -17
  35. package/css/chessground-examples/yarn.lock +0 -3529
@@ -1,46 +1,58 @@
1
- import { Api } from 'chessground/api';
1
+ import { Api } from "chessground/api";
2
+ import { Color, Key } from "chessground/types";
3
+ import { ChessInstance, SQUARES } from "chess.js";
2
4
 
3
- export function toDests(chess: any) {
4
- const dests = {};
5
- chess.SQUARES.forEach(s => {
6
- const ms = chess.moves({square: s, verbose: true});
7
- if (ms.length) dests[s] = ms.map(m => m.to);
5
+ export function toDests(chess: ChessInstance): Map<Key, Key[]> {
6
+ const dests = new Map();
7
+ SQUARES.forEach((s) => {
8
+ const ms = chess.moves({ square: s, verbose: true });
9
+ if (ms.length)
10
+ dests.set(
11
+ s,
12
+ ms.map((m) => m.to),
13
+ );
8
14
  });
9
15
  return dests;
10
16
  }
11
17
 
12
- export function toColor(chess: any) {
13
- return (chess.turn() === 'w') ? 'white' : 'black';
14
-
18
+ export function toColor(chess: ChessInstance): Color {
19
+ return chess.turn() === "w" ? "white" : "black";
15
20
  }
16
21
 
17
22
  export function playOtherSide(cg: Api, chess) {
18
23
  return (orig, dest) => {
19
- chess.move({from: orig, to: dest});
24
+ chess.move({ from: orig, to: dest });
20
25
  cg.set({
21
26
  turnColor: toColor(chess),
22
27
  movable: {
23
28
  color: toColor(chess),
24
- dests: toDests(chess)
25
- }
29
+ dests: toDests(chess),
30
+ },
26
31
  });
27
32
  };
28
33
  }
29
34
 
30
- export function aiPlay(cg: Api, chess, delay: number, firstMove: boolean) {
35
+ export function aiPlay(
36
+ cg: Api,
37
+ chess: ChessInstance,
38
+ delay: number,
39
+ firstMove: boolean,
40
+ ) {
31
41
  return (orig, dest) => {
32
- chess.move({from: orig, to: dest});
42
+ chess.move({ from: orig, to: dest });
33
43
  setTimeout(() => {
34
- const moves = chess.moves({verbose:true});
35
- const move = firstMove ? moves[0] : moves[Math.floor(Math.random() * moves.length)];
44
+ const moves = chess.moves({ verbose: true });
45
+ const move = firstMove
46
+ ? moves[0]
47
+ : moves[Math.floor(Math.random() * moves.length)];
36
48
  chess.move(move.san);
37
49
  cg.move(move.from, move.to);
38
50
  cg.set({
39
51
  turnColor: toColor(chess),
40
52
  movable: {
41
53
  color: toColor(chess),
42
- dests: toDests(chess)
43
- }
54
+ dests: toDests(chess),
55
+ },
44
56
  });
45
57
  cg.playPremove();
46
58
  }, delay);
@@ -1,9 +1,8 @@
1
1
  {
2
- "include": [
3
- "src/*.ts"
4
- ],
2
+ "include": ["src/*.ts"],
5
3
  "compilerOptions": {
6
4
  "outDir": "./dist",
5
+ "rootDir": "./src",
7
6
  "noImplicitAny": false,
8
7
  "strictNullChecks": true,
9
8
  "noUnusedLocals": true,
@@ -12,6 +11,9 @@
12
11
  "noImplicitReturns": true,
13
12
  "noImplicitThis": true,
14
13
  "noUnusedParameters": true,
15
- "target": "es6"
14
+ "moduleResolution": "node",
15
+ "target": "ES2018",
16
+ "module": "esnext",
17
+ "lib": ["ES2018", "DOM"]
16
18
  }
17
19
  }
@@ -33,6 +33,7 @@
33
33
  .ssb-chess-miniboard-controls {
34
34
  display: flex;
35
35
  justify-content: center;
36
+ position: relative;
36
37
  }
37
38
 
38
39
  .ssb-chess-miniboard-control {
package/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  const m = require('mithril');
2
- const onceTrue = require('mutant/once-true');
3
-
4
2
  const MainCtrl = require('ssb-chess');
3
+ const util = require('util');
5
4
 
6
5
  const MiniboardListComponent = require('./ui/miniboard/miniboard_list');
7
6
  const NavigationBar = require('./ui/pageLayout/navigation');
@@ -15,66 +14,71 @@ const Notifier = require('./ui/notify/notifier');
15
14
  module.exports = (attachToElement, dataAccess, opts = {}) => {
16
15
  const { initialView } = opts;
17
16
 
18
- const cssFiles = [
19
- './css/global.css',
20
- './css/chessground-examples/assets/chessground.css',
21
- './css/chessground-examples/assets/theme.css',
22
- './css/board-theme.css',
23
- './css/miniboards.css',
24
- './css/largeBoard.css',
25
- './css/invites.css',
26
- './css/loading.css',
27
- './css/promote.css',
28
- './css/game.css',
29
- './css/historyArea.css',
30
- './css/playerProfiles.css',
31
- './css/actionButtons.css',
32
- './css/activity.css',
33
- './css/pgnExport.css',
34
- './css/nextPrevious.css'
35
- ];
36
-
37
- // h4cky0 strikes again? mebbe there's a better way? ;x
38
- function cssFilesToStyleTag(dom) {
17
+ // function cssFileElements() {
18
+ // const styles = m('div', {}, cssPaths().map(file => m('link', { rel: 'stylesheet', href: file })));
19
+ // return styles
20
+ // }
21
+
22
+ function cssPaths() {
23
+ const cssFiles = [
24
+ './css/global.css',
25
+ './css/chessground-examples/assets/chessground.css',
26
+ './css/chessground-examples/assets/theme.css',
27
+ './css/board-theme.css',
28
+ './css/miniboards.css',
29
+ './css/largeBoard.css',
30
+ './css/invites.css',
31
+ './css/loading.css',
32
+ './css/promote.css',
33
+ './css/game.css',
34
+ './css/historyArea.css',
35
+ './css/playerProfiles.css',
36
+ './css/actionButtons.css',
37
+ './css/activity.css',
38
+ './css/pgnExport.css',
39
+ './css/nextPrevious.css'
40
+ ];
39
41
 
40
42
  const rootDir = `${__dirname}/`;
41
-
42
- const styles = m('div', {}, cssFiles.map(file => m('link', { rel: 'stylesheet', href: rootDir + file })));
43
-
44
- m.render(dom, styles);
43
+ return cssFiles.map(file => rootDir + file)
45
44
  }
46
45
 
47
- function renderPageTop(parent, mainCtrl, settingsCtrl) {
48
- const navBar = NavigationBar(mainCtrl, settingsCtrl);
46
+ function appendCss() {
49
47
 
50
- const TopComponent = {
51
- view: () => m('div', [
52
- m(navBar),
53
- ]),
54
- };
48
+ cssPaths().forEach((filepath) => {
49
+ const link = document.createElement('link');
55
50
 
56
- m.mount(parent, TopComponent);
51
+ link.setAttribute('rel', 'stylesheet');
52
+ link.setAttribute('href', filepath);
53
+ document.head.appendChild(link);
54
+ })
57
55
  }
58
56
 
59
- function appRouter(mainBody, mainCtrl, settingsCtrl) {
57
+ async function appRouter(mountElement, mainCtrl, settingsCtrl) {
58
+ appendCss();
59
+
60
60
  const gamesInProgressObs = mainCtrl.getGameCtrl().getMyGamesInProgress();
61
61
  const gamesMyMoveObs = mainCtrl.getGameCtrl().getGamesWhereMyMove();
62
62
  const observableGamesObs = mainCtrl.getGameCtrl().getFriendsObservableGames();
63
63
  const userRecentActivity = mainCtrl.getRecentActivityCtrl().getRecentActivityForUserGames();
64
64
 
65
65
  // Hack: keep observables loaded with the latest value.
66
- gamesInProgressObs(e => e);
67
- gamesMyMoveObs(e => e);
68
- observableGamesObs(e => e);
69
- userRecentActivity(e => e);
66
+ const watches = [
67
+ gamesInProgressObs(e => e),
68
+ gamesMyMoveObs(),
69
+ observableGamesObs(e => e),
70
+ userRecentActivity(e => e)
71
+ ];
70
72
 
71
73
  const defaultView = initialView || '/my_games';
72
74
 
73
- m.route.prefix('?');
75
+ m.route.prefix = '?';
76
+
77
+ const navigationBar = NavigationBar(mainCtrl, settingsCtrl)
74
78
 
75
- m.route(mainBody, defaultView, {
76
- '/my_games': MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent()),
77
- '/games_my_move': MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent()),
79
+ m.route(mountElement, defaultView, {
80
+ '/my_games': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesInProgressObs, mainCtrl.getMyIdent())),
81
+ '/games_my_move': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, gamesMyMoveObs, mainCtrl.getMyIdent())),
78
82
  '/games/:gameId': {
79
83
  onmatch(args) {
80
84
  const gameId = atob(args.gameId);
@@ -86,67 +90,72 @@ module.exports = (attachToElement, dataAccess, opts = {}) => {
86
90
  observableGames: observableGamesObs
87
91
  }
88
92
 
89
- // Only load the game page once we have the initial game situation state.
90
- // The mithril router allows us to return a component in a promise.
91
- return new Promise((resolve) => {
92
- onceTrue(gameSituationObs, () => {
93
- const gameComponent = GameComponent(dataAccess, mainCtrl, observables, settingsCtrl);
94
- resolve(gameComponent);
95
- });
96
- });
93
+ return GameComponent(gameId, dataAccess, mainCtrl, observables, settingsCtrl).then((gameComponent) => {
94
+ return layoutTemplate(navigationBar, gameComponent);
95
+ })
96
+
97
97
  },
98
98
  },
99
- '/invitations': InvitationsComponent(mainCtrl),
100
- '/activity': RecentActivityComponent(mainCtrl, userRecentActivity),
101
- '/observable': MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent()),
102
- '/player/:playerId': PlayerProfileComponent(mainCtrl),
99
+ '/invitations': layoutTemplate(navigationBar, InvitationsComponent(mainCtrl)),
100
+ '/activity': layoutTemplate(navigationBar, RecentActivityComponent(mainCtrl, userRecentActivity)),
101
+ '/observable': layoutTemplate(navigationBar, MiniboardListComponent(mainCtrl, observableGamesObs, mainCtrl.getMyIdent())),
102
+ '/player/:playerId': layoutTemplate(navigationBar, PlayerProfileComponent(mainCtrl)),
103
103
  '/games/:gameId/pgn': {
104
104
  onmatch(args) {
105
105
  const gameId = atob(args.gameId);
106
106
  return mainCtrl.getPgnCtrl()
107
107
  .getPgnExport(gameId)
108
- .then(pgnText => PgnExportComponent(gameId, pgnText));
108
+ .then(pgnText => layoutTemplate(navigationBar, PgnExportComponent(gameId, pgnText)));
109
109
  },
110
110
  },
111
111
  });
112
112
 
113
+ return watches;
113
114
  }
114
115
 
115
- dataAccess.whoAmI((err, ident) => {
116
- const mainCtrl = MainCtrl(dataAccess, ident.id);
117
-
118
- const settingsCtrl = mainCtrl.getSettingsCtrl();
119
-
120
- const mainBody = document.createElement('div');
121
- mainBody.className = 'ssb-chess-container'
122
-
123
- attachToElement.appendChild(mainBody);
124
-
125
- const navDiv = document.createElement('div');
126
- navDiv.id = 'ssb-nav';
127
- const bodyDiv = document.createElement('div');
116
+ function layoutTemplate(navigationBarComponent, component) {
117
+
118
+ // TODO: the nav bar should accept observables by injection and we should give it
119
+ // the same instances on each render so there's not jitter on the numbers in the nav items
120
+ return {
121
+ view: function() {
122
+ return m('div', {className: 'ssb-chess-container'}, [
123
+ m(navigationBarComponent),
124
+ m('div', m(component))
125
+ ])
126
+
127
+
128
+ }
129
+ }
128
130
 
129
- const cssDiv = document.createElement('div');
130
- cssFilesToStyleTag(cssDiv);
131
+ }
131
132
 
132
- mainBody.appendChild(cssDiv);
133
- mainBody.appendChild(navDiv);
134
- mainBody.appendChild(bodyDiv);
133
+ const whoAmIAsync = util.promisify(dataAccess.whoAmI.bind(dataAccess))
135
134
 
136
- renderPageTop(navDiv, mainCtrl, settingsCtrl);
135
+ const globalWatches = whoAmIAsync().then((ident) => {
136
+ const mainCtrl = MainCtrl(dataAccess, ident.id);
137
+ const settingsCtrl = mainCtrl.getSettingsCtrl();
137
138
 
138
139
  // Display HTML5 notifications if the user is not viewing the chess app
139
140
  // and one of their games has an update.
140
141
  const notifier = Notifier(mainCtrl);
141
142
  notifier.startNotifying();
142
143
 
143
- appRouter(bodyDiv, mainCtrl, settingsCtrl);
144
- });
144
+ return appRouter(attachToElement, mainCtrl, settingsCtrl);
145
+ })
145
146
 
146
147
  return {
147
148
  goToGame: (gameId) => {
148
149
  const gameRoute = `/games/${btoa(gameId)}`;
149
150
  m.route.set(gameRoute);
150
151
  },
152
+ destroy: () => {
153
+ // unmount the app
154
+ m.route(attachToElement, null);
155
+ globalWatches.then((watches) => watches.forEach(w => w()))
156
+ }
151
157
  };
152
158
  };
159
+
160
+
161
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ssb-chess-mithril",
3
- "version": "2.0.9",
3
+ "version": "2.1.1",
4
4
  "description": "An ssb-chess client",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,17 +14,17 @@
14
14
  "author": "Gordon Martin",
15
15
  "license": "GPL-3.0",
16
16
  "dependencies": {
17
- "chessground": "7.6.8",
17
+ "@lichess-org/chessground": "^10.4.2",
18
18
  "howler": "^2.0.15",
19
19
  "human-time": "0.0.2",
20
20
  "hyperscript": "^2.0.2",
21
- "mithril": "1.1.7",
21
+ "mithril": "2.3.6",
22
22
  "mutant": "3.29.0",
23
23
  "pubsub-js": "^1.7.0",
24
24
  "pull-scroll": "^1.0.9",
25
25
  "pull-stream": "^3.6.14",
26
26
  "ramda": "^0.25.0",
27
- "ssb-chess": "4.0.5",
27
+ "ssb-chess": "4.0.7",
28
28
  "ssb-embedded-chat": "2.0.2"
29
29
  }
30
30
  }
@@ -1,7 +1,7 @@
1
1
  const m = require('mithril');
2
2
  const watchAll = require('mutant/watch-all');
3
3
 
4
- const { opposite } = require('chessground/util');
4
+ const { opposite } = require('@lichess-org/chessground/util');
5
5
 
6
6
  const R = require('ramda');
7
7
 
@@ -0,0 +1,51 @@
1
+
2
+ const m = require('mithril')
3
+ const EmbeddedChat = require('ssb-embedded-chat');
4
+ const pull = require('pull-stream');
5
+
6
+ module.exports = function ChatBox(myIdent, dataAccess, originalSituation) {
7
+
8
+ return {
9
+ view: () => {
10
+ return m('div', {className: 'ssb-chess-chat'});
11
+ },
12
+ oncreate: function (vNode) {
13
+ this.embeddedChat = makeEmbeddedChat(myIdent, dataAccess, originalSituation);
14
+ vNode.dom.appendChild(this.embeddedChat.getChatboxElement());
15
+ },
16
+ onremove: function() {
17
+ this.embeddedChat.destroy();
18
+ }
19
+ }
20
+ }
21
+
22
+ function makeEmbeddedChat(myIdent, dataAccess, situation) {
23
+ const config = {
24
+ rootMessageId: situation.gameId,
25
+ chatMessageType: 'chess_chat',
26
+ chatMessageField: 'msg',
27
+ chatboxEnabled: true,
28
+ previousChatId: situation.rematchFrom
29
+ };
30
+
31
+ if (situation.players[myIdent] != null) {
32
+ config.isPublic = false;
33
+ config.participants = Object.keys(situation.players);
34
+ } else {
35
+ config.isPublic = true;
36
+ }
37
+
38
+ config.publishPublic = dataAccess.publishPublicChessMessage.bind(dataAccess);
39
+ config.publishPrivate = dataAccess.publishPrivateChessMessage.bind(dataAccess);
40
+
41
+ config.getChatStream = (gameId, live) =>
42
+ pull(dataAccess.allGameMessages(gameId, live), pull.filter(msg => !msg.sync && msg.value && msg.value.content.type == "chess_chat"));
43
+
44
+ config.aboutSelfChangeStream = (since) => dataAccess.aboutSelfChangesUserIds(since);
45
+
46
+ config.getDisplayName = (id, cb) => dataAccess.getPlayerDisplayName(id, cb);
47
+
48
+ const chat = EmbeddedChat(config);
49
+
50
+ return chat;
51
+ }
@@ -7,7 +7,11 @@ const R = require('ramda');
7
7
  const UserLocationUtils = require('../viewer_perspective/user_location')();
8
8
 
9
9
  module.exports = (gameObservable) => {
10
- let watchesToClear = [];
10
+ // TODO(hack): the router calls this function inside a 'onceTrue' which releases the observable once it exits the function body,
11
+ // at that point the observable would have no subscribers left and would have an initial value of null in the places it is used,
12
+ // so subscribing immediately stops that happening
13
+ const unwatch = gameObservable(e => e);
14
+ let watchesToClear = [unwatch];
11
15
 
12
16
  let moveNumberSelected = 'live';
13
17
  const moveSelectedObservable = Value(moveNumberSelected);