ssb-chess-mithril 2.1.0 → 2.1.2
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/css/chessground-examples/README.md +14 -4
- package/css/chessground-examples/assets/3d.css +24 -23
- package/css/chessground-examples/assets/chessground.css +73 -15
- package/css/chessground-examples/assets/examples.css +1 -1
- package/css/chessground-examples/assets/theme.css +37 -26
- package/css/chessground-examples/index.html +5 -5
- package/css/chessground-examples/package-lock.json +1747 -8086
- package/css/chessground-examples/package.json +32 -17
- package/css/chessground-examples/pnpm-lock.yaml +874 -0
- package/css/chessground-examples/rollup.config.mjs +20 -0
- package/css/chessground-examples/src/main.ts +81 -48
- package/css/chessground-examples/src/units/anim.ts +63 -42
- package/css/chessground-examples/src/units/basics.ts +73 -13
- package/css/chessground-examples/src/units/fen.ts +21 -18
- package/css/chessground-examples/src/units/in3d.ts +26 -26
- package/css/chessground-examples/src/units/perf.ts +29 -15
- package/css/chessground-examples/src/units/play.ts +71 -52
- package/css/chessground-examples/src/units/svg.ts +230 -102
- package/css/chessground-examples/src/units/unit.ts +47 -18
- package/css/chessground-examples/src/units/viewOnly.ts +11 -11
- package/css/chessground-examples/src/units/zh.ts +25 -23
- package/css/chessground-examples/src/util.ts +30 -18
- package/css/chessground-examples/tsconfig.json +6 -4
- package/css/miniboards.css +1 -0
- package/index.js +87 -78
- package/package.json +4 -4
- package/ui/game/PieceGraveyard.js +1 -1
- package/ui/game/chat.js +3 -3
- package/ui/game/gameHistory.js +5 -1
- package/ui/game/gameView.js +30 -11
- package/ui/miniboard/miniboard.js +8 -5
- package/ui/pageLayout/navigation.js +11 -7
- package/css/chessground-examples/gulpfile.js +0 -52
- package/css/chessground-examples/index.standalone.html +0 -17
- package/css/chessground-examples/yarn.lock +0 -3529
|
@@ -1,46 +1,58 @@
|
|
|
1
|
-
import { Api } from
|
|
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:
|
|
4
|
-
const dests =
|
|
5
|
-
|
|
6
|
-
const ms = chess.moves({square: s, verbose: true});
|
|
7
|
-
if (ms.length)
|
|
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:
|
|
13
|
-
return
|
|
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(
|
|
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
|
|
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
|
-
"
|
|
14
|
+
"moduleResolution": "node",
|
|
15
|
+
"target": "ES2018",
|
|
16
|
+
"module": "esnext",
|
|
17
|
+
"lib": ["ES2018", "DOM"]
|
|
16
18
|
}
|
|
17
19
|
}
|
package/css/miniboards.css
CHANGED
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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
|
|
48
|
-
const navBar = NavigationBar(mainCtrl, settingsCtrl);
|
|
46
|
+
function appendCss() {
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
m(navBar),
|
|
53
|
-
]),
|
|
54
|
-
};
|
|
48
|
+
cssPaths().forEach((filepath) => {
|
|
49
|
+
const link = document.createElement('link');
|
|
55
50
|
|
|
56
|
-
|
|
51
|
+
link.setAttribute('rel', 'stylesheet');
|
|
52
|
+
link.setAttribute('href', filepath);
|
|
53
|
+
document.head.appendChild(link);
|
|
54
|
+
})
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
function appRouter(
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
const watches = [
|
|
67
|
+
gamesInProgressObs(e => e),
|
|
68
|
+
gamesMyMoveObs(e => e),
|
|
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(
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
130
|
-
cssFilesToStyleTag(cssDiv);
|
|
131
|
+
}
|
|
131
132
|
|
|
132
|
-
|
|
133
|
-
mainBody.appendChild(navDiv);
|
|
134
|
-
mainBody.appendChild(bodyDiv);
|
|
133
|
+
const whoAmIAsync = util.promisify(dataAccess.whoAmI.bind(dataAccess))
|
|
135
134
|
|
|
136
|
-
|
|
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(
|
|
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.mount(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.1.
|
|
3
|
+
"version": "2.1.2",
|
|
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": "
|
|
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": "
|
|
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.
|
|
27
|
+
"ssb-chess": "4.0.7",
|
|
28
28
|
"ssb-embedded-chat": "2.0.2"
|
|
29
29
|
}
|
|
30
30
|
}
|
package/ui/game/chat.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
const m = require('mithril')
|
|
3
3
|
const EmbeddedChat = require('ssb-embedded-chat');
|
|
4
|
-
const pull = require('pull-stream')
|
|
4
|
+
const pull = require('pull-stream');
|
|
5
|
+
|
|
6
|
+
module.exports = function ChatBox(myIdent, dataAccess, originalSituation) {
|
|
5
7
|
|
|
6
|
-
module.exports = function ChatBox(myIdent, dataAccess, situationObservable) {
|
|
7
8
|
return {
|
|
8
9
|
view: () => {
|
|
9
10
|
return m('div', {className: 'ssb-chess-chat'});
|
|
10
11
|
},
|
|
11
12
|
oncreate: function (vNode) {
|
|
12
|
-
const originalSituation = situationObservable();
|
|
13
13
|
this.embeddedChat = makeEmbeddedChat(myIdent, dataAccess, originalSituation);
|
|
14
14
|
vNode.dom.appendChild(this.embeddedChat.getChatboxElement());
|
|
15
15
|
},
|
package/ui/game/gameHistory.js
CHANGED
|
@@ -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
|
-
|
|
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);
|
package/ui/game/gameView.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
const m = require('mithril');
|
|
2
|
-
const { Chessground } = require('chessground');
|
|
2
|
+
const { Chessground } = require('@lichess-org/chessground');
|
|
3
3
|
const PubSub = require('pubsub-js');
|
|
4
4
|
const Value = require('mutant/value');
|
|
5
5
|
const watchAll = require('mutant/watch-all');
|
|
6
6
|
const computed = require('mutant/computed');
|
|
7
7
|
const when = require('mutant/when');
|
|
8
8
|
const { Howl } = require('howler');
|
|
9
|
+
const watch = require('mutant/watch');
|
|
9
10
|
|
|
10
11
|
const ActionButtons = require('./gameActions');
|
|
11
12
|
const GameHistory = require('./gameHistory');
|
|
@@ -15,10 +16,28 @@ const ChatBox = require('./chat')
|
|
|
15
16
|
const PieceGraveyard = require('./PieceGraveyard');
|
|
16
17
|
const NextPreviousButtons = require('./nextGameControl');
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
function getInitialSituation(situationObservable) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const w = watch(situationObservable, (situation) => {
|
|
22
|
+
if (situation) {
|
|
23
|
+
resolve(situation)
|
|
24
|
+
|
|
25
|
+
// hack: Keep the observable warm - the ssb-chess 'getSituationObservable' doesn't handle
|
|
26
|
+
// the observable having 0 listeners then 1 again - it aborts the underyling stream when it
|
|
27
|
+
// reaches 0 listeners but doesn't start it again if it reaches >= 1 again.
|
|
28
|
+
// TODO: fix this properly
|
|
29
|
+
setTimeout(w, 50000);
|
|
30
|
+
}
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = async (gameId, dataAccess, gameCtrl, observables, settings) => {
|
|
20
37
|
|
|
38
|
+
const myIdent = gameCtrl.getMyIdent();
|
|
21
39
|
const { situationObservable, gamesWhereMyMove, observableGames } = observables;
|
|
40
|
+
const originalSituation = await getInitialSituation(situationObservable);
|
|
22
41
|
|
|
23
42
|
let chessGround = null;
|
|
24
43
|
const chessGroundObservable = Value();
|
|
@@ -75,8 +94,8 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
|
|
|
75
94
|
|
|
76
95
|
function isPromotionMove(cg, dest) {
|
|
77
96
|
return (dest[1] === '8' || dest[1] === '1')
|
|
78
|
-
&& cg.state.pieces
|
|
79
|
-
&& (cg.state.pieces
|
|
97
|
+
&& cg.state.pieces.get(dest)
|
|
98
|
+
&& (cg.state.pieces.get(dest).role === 'pawn');
|
|
80
99
|
}
|
|
81
100
|
|
|
82
101
|
function renderBoard(gameId) {
|
|
@@ -131,18 +150,21 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
|
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
function situationToChessgroundConfig(situation, moveSelected, validMoves) {
|
|
153
|
+
const ended = ["mate", "draw", "resigned"].indexOf(situation.status.status) !== -1;
|
|
154
|
+
|
|
134
155
|
const playerColour = situation.players[myIdent] ? situation.players[myIdent].colour : 'white';
|
|
135
156
|
|
|
136
157
|
const colourToPlay = plyToColourToPlay(situation.ply);
|
|
137
158
|
|
|
138
159
|
const config = {
|
|
160
|
+
viewOnly: ended,
|
|
139
161
|
fen: situation.fen,
|
|
140
162
|
orientation: playerColour,
|
|
141
163
|
turnColor: colourToPlay,
|
|
142
164
|
ply: situation.ply,
|
|
143
165
|
check: situation.check,
|
|
144
166
|
movable: {
|
|
145
|
-
dests: validMoves,
|
|
167
|
+
dests: new Map(Object.entries(validMoves)),
|
|
146
168
|
free: false,
|
|
147
169
|
color: situation.toMove === myIdent ? playerColour : null,
|
|
148
170
|
events: {
|
|
@@ -225,12 +247,11 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
|
|
|
225
247
|
}
|
|
226
248
|
}
|
|
227
249
|
|
|
228
|
-
const Chat = ChatBox(myIdent, dataAccess,
|
|
250
|
+
const Chat = ChatBox(myIdent, dataAccess, originalSituation);
|
|
229
251
|
|
|
230
252
|
return {
|
|
231
253
|
|
|
232
254
|
view(ctrl) {
|
|
233
|
-
const gameId = atob(ctrl.attrs.gameId);
|
|
234
255
|
|
|
235
256
|
return m('div', {
|
|
236
257
|
class: 'ssb-chess-board-background-blue3 merida ssb-chess-game-layout',
|
|
@@ -243,13 +264,11 @@ module.exports = (dataAccess, gameCtrl, observables, settings) => {
|
|
|
243
264
|
])]);
|
|
244
265
|
},
|
|
245
266
|
oncreate(vNode) {
|
|
246
|
-
const gameId = atob(vNode.attrs.gameId);
|
|
247
267
|
const boardDom = document.getElementById(gameId);
|
|
248
268
|
|
|
249
|
-
const originalSituation = situationObservable();
|
|
250
|
-
|
|
251
269
|
const config = situationToChessgroundConfig(originalSituation, 'live', {});
|
|
252
270
|
chessGround = Chessground(boardDom, config);
|
|
271
|
+
|
|
253
272
|
chessGroundObservable.set(chessGround);
|
|
254
273
|
|
|
255
274
|
const validMovesObservable = gameCtrl.getMovesFinderCtrl()
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const m = require('mithril');
|
|
2
|
-
const { Chessground } = require('chessground');
|
|
2
|
+
const { Chessground } = require('@lichess-org/chessground');
|
|
3
3
|
const timeAgo = require('./timeAgo');
|
|
4
4
|
|
|
5
5
|
module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
|
|
@@ -13,9 +13,10 @@ module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
|
|
|
13
13
|
&& summary.players[identPerspective].colour) ? summary.players[identPerspective].colour : 'white';
|
|
14
14
|
|
|
15
15
|
function renderPlayerName(player) {
|
|
16
|
-
|
|
16
|
+
const link = `/player/${btoa(player.id)}`;
|
|
17
|
+
return m(m.route.Link, {
|
|
17
18
|
class: 'ssb-chess-miniboard-name',
|
|
18
|
-
|
|
19
|
+
href: link,
|
|
19
20
|
},
|
|
20
21
|
player.name.substring(0, 10));
|
|
21
22
|
}
|
|
@@ -48,14 +49,16 @@ module.exports = (gameSummaryObservable, summary, identPerspective, opts) => {
|
|
|
48
49
|
const observing = Object.keys(summary.players).indexOf(identPerspective) === -1;
|
|
49
50
|
const boardSizeClass = opts && opts.small ? 'ssb-chess-board-small' : 'ssb-chess-board-medium';
|
|
50
51
|
|
|
52
|
+
const link = `/games/${btoa(summary.gameId)}?observing=${observing}`;
|
|
53
|
+
|
|
51
54
|
return m('div', {
|
|
52
55
|
class: 'ssb-chess-miniboard ssb-chess-board-background-blue3 merida',
|
|
53
56
|
}, [
|
|
54
|
-
m(
|
|
57
|
+
m(m.route.Link, {
|
|
55
58
|
class: `ssb-chessground-container cg-wrap ${boardSizeClass}`,
|
|
56
59
|
title: summary.gameId,
|
|
57
60
|
id: summary.gameId,
|
|
58
|
-
|
|
61
|
+
href: link,
|
|
59
62
|
}), renderSummaryBottom(),
|
|
60
63
|
]);
|
|
61
64
|
}
|
|
@@ -2,7 +2,7 @@ const m = require('mithril');
|
|
|
2
2
|
const SettingsDialog = require('../settings/settings-dialog');
|
|
3
3
|
|
|
4
4
|
module.exports = (mainCtrl, settings) => {
|
|
5
|
-
m.route.prefix
|
|
5
|
+
m.route.prefix = '?';
|
|
6
6
|
|
|
7
7
|
const gamesInProgress = {
|
|
8
8
|
name: 'Games',
|
|
@@ -45,8 +45,8 @@ module.exports = (mainCtrl, settings) => {
|
|
|
45
45
|
function renderNavItem(navItem) {
|
|
46
46
|
return m('span', {
|
|
47
47
|
class: 'ssb-chess-nav-item',
|
|
48
|
-
}, m(
|
|
49
|
-
|
|
48
|
+
}, m(m.route.Link, {
|
|
49
|
+
href: navItem.link,
|
|
50
50
|
title: navItem.countHoverText(),
|
|
51
51
|
},
|
|
52
52
|
|
|
@@ -89,24 +89,28 @@ module.exports = (mainCtrl, settings) => {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
function keepCountsUpdated() {
|
|
92
|
-
navItems.
|
|
92
|
+
const watches = navItems.map((navItem) => {
|
|
93
93
|
if (navItem.countUpdateFn) {
|
|
94
|
-
navItem.countUpdateFn()((items) => {
|
|
94
|
+
return navItem.countUpdateFn()((items) => {
|
|
95
95
|
const numItems = items.length;
|
|
96
96
|
navItem.count = numItems;
|
|
97
97
|
m.redraw();
|
|
98
98
|
});
|
|
99
|
+
} else {
|
|
100
|
+
return () => {}
|
|
99
101
|
}
|
|
100
102
|
});
|
|
103
|
+
|
|
104
|
+
return watches;
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
return {
|
|
104
108
|
view: () => renderNavigation(),
|
|
105
109
|
oncreate: () => {
|
|
106
|
-
keepCountsUpdated();
|
|
110
|
+
this.watches = keepCountsUpdated();
|
|
107
111
|
},
|
|
108
112
|
onremove: () => {
|
|
109
|
-
|
|
113
|
+
this.watches.forEach(watch => watch());
|
|
110
114
|
},
|
|
111
115
|
};
|
|
112
116
|
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const gulp = require('gulp');
|
|
2
|
-
const source = require('vinyl-source-stream');
|
|
3
|
-
const buffer = require('vinyl-buffer');
|
|
4
|
-
const colors = require('ansi-colors');
|
|
5
|
-
const logger = require('fancy-log');
|
|
6
|
-
const watchify = require('watchify');
|
|
7
|
-
const browserify = require('browserify');
|
|
8
|
-
const uglify = require('gulp-uglify');
|
|
9
|
-
const tsify = require('tsify');
|
|
10
|
-
|
|
11
|
-
const destination = gulp.dest('./dist');
|
|
12
|
-
const fileBaseName = 'chessground-examples';
|
|
13
|
-
|
|
14
|
-
const browserifyOpts = (debug) => ({
|
|
15
|
-
entries: ['src/main.ts'],
|
|
16
|
-
standalone: 'ChessgroundExamples',
|
|
17
|
-
debug: debug
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const prod = () => browserify(browserifyOpts(false))
|
|
21
|
-
.plugin(tsify)
|
|
22
|
-
.bundle()
|
|
23
|
-
.pipe(source(`${fileBaseName}.min.js`))
|
|
24
|
-
.pipe(buffer())
|
|
25
|
-
.pipe(uglify())
|
|
26
|
-
.pipe(destination);
|
|
27
|
-
|
|
28
|
-
const dev = () => browserify(browserifyOpts(true))
|
|
29
|
-
.plugin(tsify)
|
|
30
|
-
.bundle()
|
|
31
|
-
.pipe(source(`${fileBaseName}.js`))
|
|
32
|
-
.pipe(destination);
|
|
33
|
-
|
|
34
|
-
const watch = () => {
|
|
35
|
-
|
|
36
|
-
const bundle = () => bundler
|
|
37
|
-
.bundle()
|
|
38
|
-
.on('error', error => logger.error(colors.red(error.message)))
|
|
39
|
-
.pipe(source(`${fileBaseName}.js`))
|
|
40
|
-
.pipe(destination);
|
|
41
|
-
|
|
42
|
-
const bundler = watchify(
|
|
43
|
-
browserify(Object.assign({}, watchify.args, browserifyOpts(true)))
|
|
44
|
-
.plugin(tsify)
|
|
45
|
-
).on('update', bundle).on('log', logger.info);
|
|
46
|
-
|
|
47
|
-
return bundle();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
gulp.task('prod', prod);
|
|
51
|
-
gulp.task('dev', dev);
|
|
52
|
-
gulp.task('default', watch);
|