@scrabble-solver/scrabble-solver 2.15.10 → 2.15.12

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 (297) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/build-manifest.json +15 -15
  3. package/.next/cache/.rscinfo +1 -1
  4. package/.next/cache/.tsbuildinfo +1 -1
  5. package/.next/cache/webpack/client-production/0.pack +0 -0
  6. package/.next/cache/webpack/client-production/index.pack +0 -0
  7. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  8. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  9. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  10. package/.next/cache/webpack/edge-server-production/index.pack.old +0 -0
  11. package/.next/cache/webpack/server-production/0.pack +0 -0
  12. package/.next/cache/webpack/server-production/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/index.pack.old +0 -0
  14. package/.next/diagnostics/framework.json +1 -1
  15. package/.next/next-minimal-server.js.nft.json +1 -1
  16. package/.next/next-server.js.nft.json +1 -1
  17. package/.next/prerender-manifest.json +4 -4
  18. package/.next/required-server-files.json +7 -6
  19. package/.next/routes-manifest.json +1 -1
  20. package/.next/server/chunks/30.js +3 -3
  21. package/.next/server/chunks/318.js +1 -0
  22. package/.next/server/chunks/60.js +1 -1
  23. package/.next/server/chunks/812.js +1 -1
  24. package/.next/server/chunks/974.js +1 -1
  25. package/.next/server/middleware-build-manifest.js +1 -1
  26. package/.next/server/pages/404.html +1 -1
  27. package/.next/server/pages/404.js.nft.json +1 -1
  28. package/.next/server/pages/500.html +1 -1
  29. package/.next/server/pages/_app.js +1 -1
  30. package/.next/server/pages/_app.js.nft.json +1 -1
  31. package/.next/server/pages/_error.js +1 -1
  32. package/.next/server/pages/_error.js.nft.json +1 -1
  33. package/.next/server/pages/api/dictionary/[locale]/[word].js.nft.json +1 -1
  34. package/.next/server/pages/api/dictionary/[locale].js.nft.json +1 -1
  35. package/.next/server/pages/api/solve.js +1 -1
  36. package/.next/server/pages/api/solve.js.nft.json +1 -1
  37. package/.next/server/pages/api/verify.js.nft.json +1 -1
  38. package/.next/server/pages/index.html +1 -1
  39. package/.next/server/pages/index.js +1 -1
  40. package/.next/server/pages/index.js.nft.json +1 -1
  41. package/.next/server/pages/index.json +1 -1
  42. package/.next/server/pages-manifest.json +1 -1
  43. package/.next/static/{ylO_ttKeJTuoqDYywao5A → X6ny8arpUxpRCZ4OMm7Vo}/_buildManifest.js +1 -1
  44. package/.next/static/chunks/framework-57125a0cc6749ff9.js +1 -0
  45. package/.next/static/chunks/main-03618e8bd3cd04dd.js +1 -0
  46. package/.next/static/chunks/pages/{404-590e2a3839c1d9e0.js → 404-18b9f6f5faa91442.js} +1 -1
  47. package/.next/static/chunks/pages/_app-2378fe90e1762e8c.js +1 -0
  48. package/.next/static/chunks/pages/index-bfc599d9351773ae.js +1 -0
  49. package/.next/static/css/{6682db14f926d4c7.css → 04a3767982ec10e8.css} +1 -1
  50. package/.next/static/css/{d875648f38121a28.css → 1fae874a25934f54.css} +1 -1
  51. package/.next/trace +23 -23
  52. package/coverage/clover.xml +6 -0
  53. package/coverage/coverage-final.json +1 -0
  54. package/coverage/lcov-report/base.css +224 -0
  55. package/coverage/lcov-report/block-navigation.js +87 -0
  56. package/coverage/lcov-report/favicon.png +0 -0
  57. package/coverage/lcov-report/index.html +101 -0
  58. package/coverage/lcov-report/prettify.css +1 -0
  59. package/coverage/lcov-report/prettify.js +2 -0
  60. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  61. package/coverage/lcov-report/sorter.js +196 -0
  62. package/coverage/lcov.info +0 -0
  63. package/package.json +19 -18
  64. package/src/api/getServerLoggingData.ts +1 -1
  65. package/src/api/isBoardValid.ts +1 -1
  66. package/src/api/isCellValid.ts +1 -1
  67. package/src/api/isRowValid.ts +1 -1
  68. package/src/components/Alert/Alert.tsx +1 -1
  69. package/src/components/Badge/Badge.tsx +1 -1
  70. package/src/components/Board/Board.tsx +8 -8
  71. package/src/components/Board/BoardPure.tsx +12 -12
  72. package/src/components/Board/components/Actions/Actions.tsx +3 -3
  73. package/src/components/Board/components/Actions/lib.ts +3 -3
  74. package/src/components/Board/components/Cell/Cell.tsx +7 -7
  75. package/src/components/Board/components/InputPrompt/InputPrompt.tsx +2 -2
  76. package/src/components/Board/components/ToggleDirectionButton/ToggleDirectionButton.tsx +2 -2
  77. package/src/components/Board/hooks/useBackgroundImage.tsx +1 -1
  78. package/src/components/Board/hooks/useBoardStyle.ts +1 -1
  79. package/src/components/Board/hooks/useGrid.ts +13 -13
  80. package/src/components/Board/lib.ts +37 -0
  81. package/src/components/Board/selectors.ts +8 -0
  82. package/src/components/Button/Button.tsx +1 -1
  83. package/src/components/Button/Link.tsx +1 -1
  84. package/src/components/Dictionary/Dictionary.tsx +10 -3
  85. package/src/components/DictionaryInput/DictionaryInput.tsx +1 -1
  86. package/src/components/EmptyState/EmptyState.tsx +3 -3
  87. package/src/components/IconButton/IconButton.tsx +1 -1
  88. package/src/components/IconButton/Link.tsx +1 -1
  89. package/src/components/Key/Key.tsx +1 -1
  90. package/src/components/Keys/Arrows/Arrows.tsx +1 -1
  91. package/src/components/Keys/index.tsx +1 -1
  92. package/src/components/Loading/Loading.tsx +6 -8
  93. package/src/components/Logo/LogoBlueprint.tsx +1 -1
  94. package/src/components/Modal/Modal.tsx +4 -2
  95. package/src/components/Modal/components/Section/Section.tsx +1 -1
  96. package/src/components/NavButtons/NavButtons.tsx +16 -12
  97. package/src/components/NavButtons/selectors.ts +11 -0
  98. package/src/components/NotFound/NotFound.tsx +1 -1
  99. package/src/components/PlainTiles/PlainTiles.tsx +1 -1
  100. package/src/components/PlainTiles/Tile.tsx +1 -1
  101. package/src/components/PlainTiles/lib.ts +90 -0
  102. package/src/components/Progress/Progress.tsx +1 -1
  103. package/src/components/Rack/Rack.tsx +10 -26
  104. package/src/components/Rack/components/InputPrompt/InputPrompt.tsx +5 -8
  105. package/src/components/Rack/components/InputPrompt/lib.test.ts +27 -0
  106. package/src/components/Rack/components/InputPrompt/lib.ts +19 -0
  107. package/src/components/Rack/components/RackTile/RackTile.tsx +9 -9
  108. package/src/components/Rack/selectors.ts +9 -0
  109. package/src/components/Radio/Radio.tsx +1 -1
  110. package/src/components/Results/Cell.tsx +2 -2
  111. package/src/components/Results/Header.tsx +1 -1
  112. package/src/components/Results/HeaderButton.tsx +2 -2
  113. package/src/components/Results/Result.tsx +5 -6
  114. package/src/components/Results/Results.tsx +6 -6
  115. package/src/components/Results/SolveButton.tsx +3 -3
  116. package/src/components/Results/types.ts +2 -2
  117. package/src/components/ResultsInput/ResultsInput.tsx +1 -1
  118. package/src/components/SeoMessage/SeoMessage.tsx +5 -4
  119. package/src/components/Solver/Solver.tsx +4 -4
  120. package/src/components/Solver/components/InsertButton/InsertButton.tsx +1 -1
  121. package/src/components/Solver/components/ResultCandidatePicker/ResultCandidatePicker.tsx +5 -5
  122. package/src/components/Spinner/Spinner.tsx +1 -1
  123. package/src/components/Tile/Tile.tsx +7 -7
  124. package/src/components/Tile/TilePure.tsx +8 -8
  125. package/src/components/Tooltip/Tooltip.tsx +1 -1
  126. package/src/components/Tooltip/TooltipContent.tsx +1 -1
  127. package/src/components/Tooltip/TooltipTrigger.tsx +1 -1
  128. package/src/components/Tooltip/context.ts +1 -1
  129. package/src/components/Tooltip/useTooltip.ts +1 -1
  130. package/src/hooks/useAppLayout.ts +1 -1
  131. package/src/hooks/useColumns.ts +28 -1
  132. package/src/hooks/useEffectOnce.ts +1 -1
  133. package/src/hooks/useLocalStorage.ts +8 -0
  134. package/src/i18n/constants.ts +1 -1
  135. package/src/i18n/i18n.ts +1 -1
  136. package/src/i18n/languages/english.json +3 -0
  137. package/src/i18n/languages/french.json +3 -0
  138. package/src/i18n/languages/german.json +3 -0
  139. package/src/i18n/languages/persian.json +3 -0
  140. package/src/i18n/languages/polish.json +3 -0
  141. package/src/i18n/languages/romanian.json +3 -0
  142. package/src/i18n/languages/spanish.json +3 -0
  143. package/src/i18n/languages/turkish.json +3 -0
  144. package/src/lib/createComparator.ts +1 -1
  145. package/src/lib/createKeyComparator.ts +1 -1
  146. package/src/lib/createKeyboardNavigation.ts +1 -1
  147. package/src/lib/createStringComparator.ts +1 -1
  148. package/src/lib/extractCharacters.test.ts +42 -15
  149. package/src/lib/extractCharacters.ts +27 -25
  150. package/src/lib/findCell.ts +1 -1
  151. package/src/lib/getCoordinate.test.ts +22 -0
  152. package/src/lib/getCoordinate.ts +9 -1
  153. package/src/lib/index.ts +2 -17
  154. package/src/lib/isCtrl.ts +1 -1
  155. package/src/lib/localeTransliterate.test.ts +14 -0
  156. package/src/lib/localeTransliterate.ts +18 -0
  157. package/src/lib/numberComparator.ts +1 -1
  158. package/src/lib/reverseComparator.ts +1 -1
  159. package/src/lib/zipCharactersAndTiles.ts +2 -2
  160. package/src/modals/DictionaryModal/DictionaryModal.tsx +1 -1
  161. package/src/modals/KeyMapModal/KeyMapModal.tsx +1 -1
  162. package/src/modals/KeyMapModal/components/Mapping/Mapping.tsx +1 -1
  163. package/src/modals/MenuModal/MenuModal.tsx +1 -1
  164. package/src/modals/RemainingTilesModal/RemainingTilesModal.tsx +3 -2
  165. package/src/modals/RemainingTilesModal/components/Character/Character.tsx +7 -2
  166. package/src/{lib/getRemainingTilesGroups.ts → modals/RemainingTilesModal/lib.ts} +26 -9
  167. package/src/modals/RemainingTilesModal/selectors.ts +7 -0
  168. package/src/modals/ResultsModal/ResultsModal.tsx +4 -4
  169. package/src/modals/SettingsModal/SettingsModal.tsx +6 -1
  170. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/AutoGroupTilesSetting.tsx +1 -1
  171. package/src/modals/SettingsModal/components/AutoGroupTilesSetting/lib.ts +1 -1
  172. package/src/modals/SettingsModal/components/ConfigSetting/ConfigSetting.tsx +1 -1
  173. package/src/modals/SettingsModal/components/ConfigSetting/lib.ts +1 -1
  174. package/src/modals/SettingsModal/components/InputModeSetting/InputModeSetting.tsx +1 -1
  175. package/src/modals/SettingsModal/components/InputModeSetting/lib.ts +1 -1
  176. package/src/modals/SettingsModal/components/LocaleSetting/LocaleSetting.tsx +2 -2
  177. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/RemoveCellFiltersSetting.module.scss +12 -0
  178. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/RemoveCellFiltersSetting.tsx +53 -0
  179. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/index.ts +1 -0
  180. package/src/modals/SettingsModal/components/RemoveCellFiltersSetting/lib.ts +13 -0
  181. package/src/modals/SettingsModal/components/ShowCoordinatesSetting/ShowCoordinatesSetting.tsx +2 -2
  182. package/src/modals/SettingsModal/components/index.ts +1 -0
  183. package/src/modals/WordsModal/WordsModal.tsx +4 -3
  184. package/src/pages/_app.tsx +4 -3
  185. package/src/pages/_document.tsx +1 -1
  186. package/src/pages/api/dictionary/[locale]/[word].ts +2 -2
  187. package/src/pages/api/dictionary/[locale]/index.ts +2 -2
  188. package/src/pages/api/solve.ts +11 -2
  189. package/src/pages/api/verify.ts +2 -2
  190. package/src/pages/api/visit.ts +1 -1
  191. package/src/pages/index.tsx +38 -44
  192. package/src/parameters/index.ts +1 -0
  193. package/src/sdk/findWordDefinitions.ts +1 -1
  194. package/src/sdk/getDictionary.ts +1 -1
  195. package/src/sdk/solve.ts +1 -1
  196. package/src/sdk/verify.ts +1 -1
  197. package/src/service-worker/dictionaries/getDictionary.ts +1 -1
  198. package/src/service-worker/dictionaries/getDictionaryUrl.ts +1 -1
  199. package/src/service-worker/dictionaries/revalidateDictionary.ts +1 -1
  200. package/src/service-worker/getTrie.ts +1 -1
  201. package/src/service-worker/routeSolveRequests.ts +2 -2
  202. package/src/service-worker/routeVerifyRequests.ts +1 -1
  203. package/src/state/board/index.ts +4 -0
  204. package/src/state/{slices/boardInitialState.ts → board/initialState.ts} +3 -6
  205. package/src/state/board/selectors.ts +3 -0
  206. package/src/state/{slices/boardSlice.ts → board/slice.ts} +5 -4
  207. package/src/state/board/types.ts +3 -0
  208. package/src/state/cellFilters/index.ts +4 -0
  209. package/src/state/cellFilters/initialState.ts +3 -0
  210. package/src/state/cellFilters/lib.ts +8 -0
  211. package/src/state/cellFilters/selectors.ts +13 -0
  212. package/src/state/{slices/cellFilterSlice.ts → cellFilters/slice.ts} +15 -14
  213. package/src/state/cellFilters/types.ts +3 -0
  214. package/src/state/dictionary/index.ts +4 -0
  215. package/src/state/dictionary/initialState.ts +8 -0
  216. package/src/state/dictionary/selectors.ts +16 -0
  217. package/src/state/{slices/dictionarySlice.ts → dictionary/slice.ts} +3 -3
  218. package/src/state/dictionary/types.ts +8 -0
  219. package/src/state/index.ts +12 -5
  220. package/src/{lib/getRemainingTiles.ts → state/lib.ts} +3 -4
  221. package/src/state/localStorage.ts +11 -2
  222. package/src/state/rack/index.ts +4 -0
  223. package/src/state/rack/initialState.ts +12 -0
  224. package/src/state/rack/selectors.ts +7 -0
  225. package/src/state/{slices/rackSlice.ts → rack/slice.ts} +4 -4
  226. package/src/state/rack/types.ts +3 -0
  227. package/src/state/results/index.ts +4 -0
  228. package/src/state/results/initialState.ts +13 -0
  229. package/src/state/results/lib.ts +96 -0
  230. package/src/state/results/selectors.ts +75 -0
  231. package/src/state/{slices/resultsSlice.ts → results/slice.ts} +4 -4
  232. package/src/state/results/types.ts +10 -0
  233. package/src/state/sagas.ts +22 -23
  234. package/src/state/selectors.ts +15 -235
  235. package/src/state/settings/index.ts +4 -0
  236. package/src/state/{slices/settingsInitialState.ts → settings/initialState.ts} +4 -11
  237. package/src/state/settings/lib.ts +42 -0
  238. package/src/state/settings/selectors.ts +69 -0
  239. package/src/state/{slices/settingsSlice.ts → settings/slice.ts} +9 -4
  240. package/src/state/settings/types.ts +12 -0
  241. package/src/state/solve/index.ts +4 -0
  242. package/src/state/solve/initialState.ts +12 -0
  243. package/src/state/solve/selectors.ts +14 -0
  244. package/src/state/{slices/solveSlice.ts → solve/slice.ts} +3 -3
  245. package/src/state/solve/types.ts +10 -0
  246. package/src/state/store.ts +9 -11
  247. package/src/state/types.ts +16 -18
  248. package/src/state/useTranslate.ts +2 -2
  249. package/src/state/useTypedSelector.ts +2 -2
  250. package/src/state/verify/index.ts +4 -0
  251. package/src/state/verify/initialState.ts +12 -0
  252. package/src/state/verify/selectors.ts +9 -0
  253. package/src/state/{slices/verifySlice.ts → verify/slice.ts} +3 -3
  254. package/src/state/verify/types.ts +10 -0
  255. package/src/styles/global.scss +5 -0
  256. package/src/types/api.ts +1 -1
  257. package/src/types/index.ts +9 -4
  258. package/tsconfig.tsbuildinfo +1 -0
  259. package/.eslintrc.js +0 -10
  260. package/.next/server/chunks/968.js +0 -1
  261. package/.next/static/chunks/framework-288d1abd95de88d9.js +0 -1
  262. package/.next/static/chunks/main-016492249b3393e2.js +0 -1
  263. package/.next/static/chunks/pages/_app-ac93a74c390f2ab8.js +0 -1
  264. package/.next/static/chunks/pages/index-e5f1caa581e6d3b8.js +0 -1
  265. package/src/components/Board/lib/getBonusColor.ts +0 -16
  266. package/src/components/Board/lib/getPositionInGrid.ts +0 -13
  267. package/src/components/Board/lib/index.ts +0 -2
  268. package/src/components/PlainTiles/lib/createPlainTile.ts +0 -41
  269. package/src/components/PlainTiles/lib/createPlainTiles.ts +0 -25
  270. package/src/components/PlainTiles/lib/getViewbox.ts +0 -17
  271. package/src/components/PlainTiles/lib/getX.ts +0 -5
  272. package/src/components/PlainTiles/lib/getY.ts +0 -5
  273. package/src/components/PlainTiles/lib/index.ts +0 -6
  274. package/src/components/PlainTiles/lib/randomize.ts +0 -1
  275. package/src/lib/createArray.ts +0 -1
  276. package/src/lib/createGridOf.ts +0 -9
  277. package/src/lib/detectLocale.ts +0 -27
  278. package/src/lib/extractCharactersByCase.test.ts +0 -29
  279. package/src/lib/extractCharactersByCase.ts +0 -29
  280. package/src/lib/getCellSize.ts +0 -10
  281. package/src/lib/getCoordinates.ts +0 -16
  282. package/src/lib/getRemainingTilesCount.ts +0 -5
  283. package/src/lib/getTotalRemainingTilesCount.ts +0 -5
  284. package/src/lib/groupResults.ts +0 -35
  285. package/src/lib/guessLocale.ts +0 -20
  286. package/src/lib/isUpperCase.ts +0 -5
  287. package/src/lib/resultMatchesCellFilter.ts +0 -21
  288. package/src/lib/sortGroupedResults.ts +0 -21
  289. package/src/lib/sortResults.ts +0 -41
  290. package/src/state/slices/cellFilterInitialState.ts +0 -5
  291. package/src/state/slices/dictionaryInitialState.ts +0 -15
  292. package/src/state/slices/index.ts +0 -16
  293. package/src/state/slices/rackInitialState.ts +0 -9
  294. package/src/state/slices/resultsInitialState.ts +0 -20
  295. package/src/state/slices/solveInitialState.ts +0 -21
  296. package/src/state/slices/verifyInitialState.ts +0 -19
  297. /package/.next/static/{ylO_ttKeJTuoqDYywao5A → X6ny8arpUxpRCZ4OMm7Vo}/_ssgManifest.js +0 -0
@@ -3,9 +3,9 @@
3
3
  /* eslint-disable @typescript-eslint/no-unsafe-call */
4
4
  /* eslint-disable @typescript-eslint/no-unsafe-member-access */
5
5
 
6
- import { PayloadAction } from '@reduxjs/toolkit';
6
+ import { type PayloadAction } from '@reduxjs/toolkit';
7
7
  import { hasConfig, languages } from '@scrabble-solver/configs';
8
- import { Board, Locale, Result } from '@scrabble-solver/types';
8
+ import { Board, type Locale, type Result } from '@scrabble-solver/types';
9
9
  import { call, delay, put, select, takeEvery, takeLatest } from 'redux-saga/effects';
10
10
 
11
11
  import { LOCALE_FEATURES } from 'i18n';
@@ -13,27 +13,21 @@ import { memoize } from 'lib';
13
13
  import { findWordDefinitions, solve, verify, visit } from 'sdk';
14
14
 
15
15
  import { initialize, reset } from './actions';
16
+ import { boardSlice, selectBoard } from './board';
17
+ import { cellFiltersSlice, selectCellFilter } from './cellFilters';
18
+ import { dictionarySlice, selectDictionary } from './dictionary';
19
+ import { rackSlice, selectCharacters, selectRack } from './rack';
20
+ import { resultsSlice } from './results';
16
21
  import {
17
- selectBoard,
18
- selectCellFilter,
19
- selectCharacters,
20
22
  selectConfig,
21
- selectDictionary,
22
23
  selectGame,
23
24
  selectLocale,
24
25
  selectLocaleAutoGroupTiles,
25
- selectRack,
26
- } from './selectors';
27
- import {
28
- boardSlice,
29
- cellFilterSlice,
30
- dictionarySlice,
31
- rackSlice,
32
- resultsSlice,
26
+ selectRemoveCellFilters,
33
27
  settingsSlice,
34
- solveSlice,
35
- verifySlice,
36
- } from './slices';
28
+ } from './settings';
29
+ import { solveSlice } from './solve';
30
+ import { verifySlice } from './verify';
37
31
 
38
32
  const SUBMIT_DELAY = 150;
39
33
 
@@ -62,7 +56,7 @@ function* onCellValueChange({ payload }: PayloadAction<{ value: string; x: numbe
62
56
  const filter = yield select((state) => selectCellFilter(state, payload));
63
57
 
64
58
  if (filter) {
65
- yield put(cellFilterSlice.actions.cancel(payload));
59
+ yield put(cellFiltersSlice.actions.cancel(payload));
66
60
  }
67
61
 
68
62
  yield put(resultsSlice.actions.changeResultCandidate(null));
@@ -75,8 +69,13 @@ function* onRackValueChange(): AnyGenerator {
75
69
 
76
70
  function* onApplyResult({ payload: result }: PayloadAction<Result>): AnyGenerator {
77
71
  const autoGroupTiles = yield select(selectLocaleAutoGroupTiles);
72
+ const removeCellFilters = yield select(selectRemoveCellFilters);
78
73
  yield put(boardSlice.actions.applyResult(result));
79
- yield put(cellFilterSlice.actions.reset());
74
+ if (removeCellFilters === 'never') {
75
+ yield put(cellFiltersSlice.actions.removeCells(result.cells));
76
+ } else {
77
+ yield put(cellFiltersSlice.actions.reset());
78
+ }
80
79
  yield put(rackSlice.actions.removeTiles(result.tiles));
81
80
  yield put(rackSlice.actions.groupTiles(autoGroupTiles));
82
81
  yield put(verifySlice.actions.submit());
@@ -92,8 +91,8 @@ function* onGameChange(): AnyGenerator {
92
91
  }
93
92
 
94
93
  yield put(resultsSlice.actions.reset());
94
+ yield* resetRack();
95
95
  yield put(verifySlice.actions.submit());
96
- yield* ensureProperTilesCount();
97
96
  }
98
97
 
99
98
  function* onDictionarySubmit(): AnyGenerator {
@@ -126,7 +125,7 @@ function* onInitialize(): AnyGenerator {
126
125
  yield call(visit);
127
126
 
128
127
  if (!board.isEmpty()) {
129
- yield* ensureProperTilesCount();
128
+ yield* resetRack();
130
129
  yield put(verifySlice.actions.submit());
131
130
  }
132
131
  }
@@ -135,7 +134,7 @@ function* onReset(): AnyGenerator {
135
134
  const config = yield select(selectConfig);
136
135
 
137
136
  yield put(boardSlice.actions.init(Board.create(config.boardWidth, config.boardHeight)));
138
- yield put(cellFilterSlice.actions.reset());
137
+ yield put(cellFiltersSlice.actions.reset());
139
138
  yield put(dictionarySlice.actions.reset());
140
139
  yield put(rackSlice.actions.reset());
141
140
  yield put(resultsSlice.actions.reset());
@@ -223,7 +222,7 @@ function* onVerify(): AnyGenerator {
223
222
  }
224
223
  }
225
224
 
226
- function* ensureProperTilesCount(): AnyGenerator {
225
+ function* resetRack(): AnyGenerator {
227
226
  const { config } = yield select(selectConfig);
228
227
  const rack = yield select(selectRack);
229
228
 
@@ -1,255 +1,35 @@
1
- /* eslint-disable max-lines */
2
-
3
1
  import { createSelector } from '@reduxjs/toolkit';
4
- import { getConfig } from '@scrabble-solver/configs';
5
- import { BLANK } from '@scrabble-solver/constants';
6
- import { Cell, Config, isError, Tile } from '@scrabble-solver/types';
7
-
8
- import { i18n, LOCALE_FEATURES } from 'i18n';
9
- import {
10
- createRegExp,
11
- findCell,
12
- getRemainingTiles,
13
- getRemainingTilesGroups,
14
- groupResults,
15
- resultMatchesCellFilter,
16
- sortGroupedResults,
17
- unorderedArraysEqual,
18
- } from 'lib';
19
- import { Point, ResultColumnId, Translations } from 'types';
20
-
21
- import { RootState } from './types';
22
-
23
- const selectCell = (_: unknown, cell: Cell): Cell => cell;
24
-
25
- const selectPoint = (_: unknown, point: Point): Point => point;
26
-
27
- const selectResultIndex = (_: unknown, index: number): number => index;
28
-
29
- const selectCharacter = (_: unknown, character: string | null): string | null => character;
30
-
31
- const selectTile = (_: unknown, tile: Tile | null): Tile | null => tile;
32
-
33
- const selectBoardRoot = (state: RootState): RootState['board'] => state.board;
34
-
35
- const selectDictionaryRoot = (state: RootState): RootState['dictionary'] => state.dictionary;
36
-
37
- const selectCellFilterRoot = (state: RootState): RootState['cellFilter'] => state.cellFilter;
38
-
39
- const selectRackRoot = (state: RootState): RootState['rack'] => state.rack;
40
-
41
- const selectResultsRoot = (state: RootState): RootState['results'] => state.results;
42
-
43
- const selectSettingsRoot = (state: RootState): RootState['settings'] => state.settings;
44
-
45
- const selectSolveRoot = (state: RootState): RootState['solve'] => state.solve;
46
-
47
- const selectVerifyRoot = (state: RootState): RootState['verify'] => state.verify;
48
-
49
- export const selectDictionary = selectDictionaryRoot;
50
-
51
- export const selectDictionaryError = createSelector([selectDictionaryRoot], (dictionary) => {
52
- return isError(dictionary.error) ? dictionary.error : undefined;
53
- });
54
-
55
- export const selectLocale = createSelector([selectSettingsRoot], (settings) => settings.locale);
56
-
57
- export const selectAutoGroupTiles = createSelector([selectSettingsRoot], (settings) => settings.autoGroupTiles);
58
-
59
- export const selectLocaleAutoGroupTiles = createSelector([selectLocale, selectSettingsRoot], (locale, settings) => {
60
- if (LOCALE_FEATURES[locale].direction === 'ltr' || settings.autoGroupTiles === null) {
61
- return settings.autoGroupTiles;
62
- }
63
-
64
- return settings.autoGroupTiles === 'left' ? 'right' : 'left';
65
- });
66
-
67
- export const selectBoard = selectBoardRoot;
68
-
69
- export const selectInputMode = createSelector([selectSettingsRoot], (settings) => settings.inputMode);
70
-
71
- export const selectShowCoordinates = createSelector([selectSettingsRoot], (settings) => settings.showCoordinates);
72
-
73
- export const selectGame = createSelector([selectSettingsRoot], (settings) => settings.game);
74
-
75
- export const selectConfig = createSelector([selectGame, selectLocale], getConfig);
76
-
77
- export const selectFilteredCells = selectCellFilterRoot;
78
-
79
- export const selectCellFilter = createSelector([selectFilteredCells, selectPoint], (cellFilter, { x, y }) => {
80
- return cellFilter.find((cell) => cell.x === x && cell.y === y);
81
- });
82
-
83
- export const selectCellIsValid = createSelector([selectConfig, selectCell], (config, cell) => {
84
- if (!cell.hasTile()) {
85
- return true;
86
- }
87
-
88
- return config.tiles.some((tile) => tile.character === cell.tile.character);
89
- });
90
-
91
- export const selectResultsRaw = createSelector([selectResultsRoot], (results) => results.results);
92
-
93
- export const selectResultsQuery = createSelector([selectResultsRoot], (results) => results.query);
94
-
95
- export const selectResultsSort = createSelector([selectResultsRoot], (results) => results.sort);
96
-
97
- export const selectGroupedResults = createSelector(
98
- [selectResultsRaw, selectResultsQuery, selectFilteredCells],
99
- groupResults,
100
- );
101
-
102
- export const selectGroupedSortedResults = createSelector(
103
- [selectGroupedResults, selectResultsSort, selectLocale, selectShowCoordinates],
104
- sortGroupedResults,
105
- );
106
-
107
- export const selectResults = createSelector([selectGroupedSortedResults], (groupedResults) => {
108
- return groupedResults ? [...groupedResults.matching, ...groupedResults.other] : groupedResults;
109
- });
110
2
 
111
- export const selectIsResultMatching = createSelector(
112
- [selectResults, selectResultsQuery, selectFilteredCells, selectResultIndex],
113
- (results, query, cellFilter, index) => {
114
- if (!results) {
115
- return false;
116
- }
3
+ import { unorderedArraysEqual } from 'lib';
117
4
 
118
- const result = results[index];
119
- const regExp = createRegExp(query);
5
+ import { selectBoard } from './board';
6
+ import { getRemainingTiles } from './lib';
7
+ import { selectCharacters } from './rack';
8
+ import { selectConfig, selectLocale } from './settings';
9
+ import { selectLastSolvedParameters } from './solve';
120
10
 
121
- if (!regExp.test(result.word)) {
122
- return false;
123
- }
124
-
125
- return resultMatchesCellFilter(result, cellFilter);
126
- },
127
- );
128
-
129
- export const selectResultCandidate = createSelector([selectResultsRoot], (results) => results.candidate);
130
-
131
- export const selectResultCandidateCells = createSelector(
132
- [selectResultCandidate],
133
- (resultCandidate): Cell[] => resultCandidate?.cells || [],
134
- );
135
-
136
- export const selectResultCandidateTiles = createSelector(
137
- [selectResultCandidate],
138
- (resultCandidate): Tile[] => resultCandidate?.tiles || [],
139
- );
140
-
141
- export const selectRowsWithCandidate = createSelector([selectBoardRoot, selectResultCandidateCells], (board, cells) => {
142
- return board.rows.map((row: Cell[], y: number) => row.map((cell: Cell, x: number) => findCell(cells, x, y) || cell));
143
- });
144
-
145
- export const selectCellBonus = createSelector([selectConfig, selectCell], (config: Config, cell: Cell) => {
146
- return config.getCellBonus(cell);
147
- });
148
-
149
- export const selectCharacterPoints = createSelector(
150
- [selectConfig, selectCharacter],
151
- (config: Config, character: string | null) => {
152
- return config.getCharacterPoints(character);
153
- },
154
- );
155
-
156
- export const selectCharacterIsValid = createSelector(
157
- [selectConfig, selectCharacter],
158
- (config: Config, character: string | null) => {
159
- if (character === null || character === BLANK) {
160
- return true;
161
- }
162
-
163
- return config.tiles.some((tile) => tile.character === character);
11
+ const selectHasBoardChanged = createSelector(
12
+ [selectLastSolvedParameters, selectBoard],
13
+ (lastSolvedParameters, board) => {
14
+ return !lastSolvedParameters.board.equals(board);
164
15
  },
165
16
  );
166
17
 
167
- export const selectTilePoints = createSelector([selectConfig, selectTile], (config: Config, tile: Tile | null) => {
168
- return config.getTilePoints(tile);
169
- });
170
-
171
- export const selectTranslations = createSelector([selectLocale], (locale) => i18n[locale]);
172
-
173
- export const selectTranslation = createSelector(
174
- [selectTranslations, selectLocale, (_: unknown, id: keyof Translations) => id],
175
- (translations, locale, id): string => {
176
- const translation = translations[id];
177
-
178
- if (typeof translation === 'undefined') {
179
- throw new Error(`Untranslated key "${id}" in locale "${locale}"`);
180
- }
181
-
182
- return translation;
183
- },
184
- );
185
-
186
- export const selectRack = selectRackRoot;
187
-
188
- export const selectCharacters = createSelector(selectRackRoot, (rack) => rack.filter((tile) => tile !== null));
189
-
190
- export const selectLastSolvedParameters = createSelector([selectSolveRoot], (solve) => solve.lastSolvedParameters);
191
-
192
- export const selectIsLoading = createSelector([selectSolveRoot], (solve) => solve.isLoading);
193
-
194
- export const selectSolveError = createSelector([selectSolveRoot], (solve) => {
195
- return isError(solve.error) ? solve.error : undefined;
196
- });
197
-
198
- export const selectHaveCharactersChanged = createSelector(
18
+ const selectHaveCharactersChanged = createSelector(
199
19
  [selectLastSolvedParameters, selectCharacters, selectLocale],
200
20
  (lastSolvedParameters, characters, locale) => {
201
21
  return !unorderedArraysEqual(lastSolvedParameters.characters, characters, locale);
202
22
  },
203
23
  );
204
24
 
205
- export const selectHasBoardChanged = createSelector(
206
- [selectLastSolvedParameters, selectBoardRoot],
207
- (lastSolvedParameters, board) => !lastSolvedParameters.board.equals(board),
208
- );
209
-
210
25
  export const selectAreResultsOutdated = createSelector(
211
26
  [selectHasBoardChanged, selectHaveCharactersChanged],
212
- (hasBoardChanged, haveCharactersChanged) => hasBoardChanged || haveCharactersChanged,
27
+ (hasBoardChanged, haveCharactersChanged) => {
28
+ return hasBoardChanged || haveCharactersChanged;
29
+ },
213
30
  );
214
31
 
215
32
  export const selectRemainingTiles = createSelector(
216
- [selectConfig, selectBoardRoot, selectCharacters, selectLocale],
33
+ [selectConfig, selectBoard, selectCharacters, selectLocale],
217
34
  getRemainingTiles,
218
35
  );
219
-
220
- export const selectHasOverusedTiles = createSelector([selectRemainingTiles], (remainingTiles) => {
221
- return remainingTiles.some(({ count, usedCount }) => usedCount > count);
222
- });
223
-
224
- export const selectRemainingTilesGroups = createSelector([selectRemainingTiles], getRemainingTilesGroups);
225
-
226
- export const selectVerify = selectVerifyRoot;
227
-
228
- export const selectHasInvalidWords = createSelector([selectVerify], ({ invalidWords }) => {
229
- return invalidWords.length > 0;
230
- });
231
-
232
- export const selectColumns = createSelector([selectLocale, selectShowCoordinates], (locale, showCoordinates) => {
233
- const { consonants, vowels } = LOCALE_FEATURES[locale];
234
- const columns: ResultColumnId[] = [
235
- ResultColumnId.Word,
236
- ResultColumnId.TilesCount,
237
- ResultColumnId.BlanksCount,
238
- ResultColumnId.WordsCount,
239
- ResultColumnId.Points,
240
- ];
241
-
242
- if (showCoordinates !== 'hidden') {
243
- columns.push(ResultColumnId.Coordinates);
244
- }
245
-
246
- if (vowels) {
247
- columns.push(ResultColumnId.VowelsCount);
248
- }
249
-
250
- if (consonants) {
251
- columns.push(ResultColumnId.ConsonantsCount);
252
- }
253
-
254
- return columns;
255
- });
@@ -0,0 +1,4 @@
1
+ export * from './initialState';
2
+ export * from './selectors';
3
+ export * from './slice';
4
+ export * from './types';
@@ -1,17 +1,9 @@
1
- import { Game, Locale, ShowCoordinates } from '@scrabble-solver/types';
2
-
3
- import { guessLocale } from 'lib';
4
- import { AutoGroupTiles, InputMode } from 'types';
1
+ import { Game } from '@scrabble-solver/types';
5
2
 
6
3
  import { localStorage } from '../localStorage';
7
4
 
8
- export interface SettingsState {
9
- autoGroupTiles: AutoGroupTiles;
10
- game: Game;
11
- inputMode: InputMode;
12
- locale: Locale;
13
- showCoordinates: ShowCoordinates;
14
- }
5
+ import { guessLocale } from './lib';
6
+ import type { SettingsState } from './types';
15
7
 
16
8
  const localStorageAutoGroupTiles = localStorage.getAutoGroupTiles();
17
9
  const isTouchScreen = typeof globalThis.matchMedia !== 'undefined' && globalThis.matchMedia('(hover: none)').matches;
@@ -22,4 +14,5 @@ export const settingsInitialState: SettingsState = {
22
14
  inputMode: localStorage.getInputMode() ?? (isTouchScreen ? 'touchscreen' : 'keyboard'),
23
15
  locale: localStorage.getLocale() ?? guessLocale(),
24
16
  showCoordinates: localStorage.getShowCoordinates() ?? 'hidden',
17
+ removeCellFilters: localStorage.getRemoveCellFilters() ?? 'always',
25
18
  };
@@ -0,0 +1,42 @@
1
+ import { Locale } from '@scrabble-solver/types';
2
+
3
+ export const guessLocale = (): Locale => {
4
+ if (!globalThis.navigator) {
5
+ return Locale.EN_US;
6
+ }
7
+
8
+ const languages = globalThis.navigator.languages;
9
+
10
+ if (languages.includes('pl') || languages.includes('pl-PL')) {
11
+ return Locale.PL_PL;
12
+ }
13
+
14
+ if (languages.includes('en-GB')) {
15
+ return Locale.EN_GB;
16
+ }
17
+
18
+ if (languages.includes('fa') || languages.includes('fa-IR')) {
19
+ return Locale.FA_IR;
20
+ }
21
+
22
+ if (languages.includes('fr-FR')) {
23
+ return Locale.FR_FR;
24
+ }
25
+
26
+ if (languages.includes('ro') || languages.includes('ro-RO')) {
27
+ return Locale.RO_RO;
28
+ }
29
+
30
+ const locales = Object.values(Locale);
31
+ const exactMatch = locales.find((locale) => globalThis.navigator.language === String(locale));
32
+
33
+ if (exactMatch) {
34
+ return exactMatch;
35
+ }
36
+
37
+ const partialMatch = locales.find((locale) => {
38
+ return globalThis.navigator.language === locale.substring(0, 2);
39
+ });
40
+
41
+ return partialMatch ?? Locale.EN_US;
42
+ };
@@ -0,0 +1,69 @@
1
+ import { createSelector } from '@reduxjs/toolkit';
2
+ import { getConfig } from '@scrabble-solver/configs';
3
+ import { BLANK } from '@scrabble-solver/constants';
4
+ import { type Cell, type Tile } from '@scrabble-solver/types';
5
+
6
+ import { i18n, LOCALE_FEATURES } from 'i18n';
7
+
8
+ import type { RootState } from '../types';
9
+
10
+ const selectCell = (_: unknown, cell: Cell): Cell => cell;
11
+
12
+ const selectCharacter = (_: unknown, character: string | null): string | null => character;
13
+
14
+ const selectTile = (_: unknown, tile: Tile | null): Tile | null => tile;
15
+
16
+ export const selectSettings = (state: RootState) => state.settings;
17
+
18
+ export const selectAutoGroupTiles = createSelector([selectSettings], (settings) => settings.autoGroupTiles);
19
+
20
+ export const selectGame = createSelector([selectSettings], (settings) => settings.game);
21
+
22
+ export const selectInputMode = createSelector([selectSettings], (settings) => settings.inputMode);
23
+
24
+ export const selectLocale = createSelector([selectSettings], (settings) => settings.locale);
25
+
26
+ export const selectLocaleAutoGroupTiles = createSelector([selectLocale, selectSettings], (locale, settings) => {
27
+ if (LOCALE_FEATURES[locale].direction === 'ltr' || settings.autoGroupTiles === null) {
28
+ return settings.autoGroupTiles;
29
+ }
30
+
31
+ return settings.autoGroupTiles === 'left' ? 'right' : 'left';
32
+ });
33
+
34
+ export const selectShowCoordinates = createSelector([selectSettings], (settings) => settings.showCoordinates);
35
+
36
+ export const selectConfig = createSelector([selectGame, selectLocale], getConfig);
37
+
38
+ export const selectCharacterPoints = createSelector([selectConfig, selectCharacter], (config, character) => {
39
+ return config.getCharacterPoints(character);
40
+ });
41
+
42
+ export const selectCharacterIsValid = createSelector([selectConfig, selectCharacter], (config, character) => {
43
+ if (character === null || character === BLANK) {
44
+ return true;
45
+ }
46
+
47
+ return config.tiles.some((tile) => tile.character === character);
48
+ });
49
+
50
+ export const selectCellBonus = createSelector([selectConfig, selectCell], (config, cell) => {
51
+ return config.getCellBonus(cell);
52
+ });
53
+
54
+ export const selectTilePoints = createSelector([selectConfig, selectTile], (config, tile) => {
55
+ return config.getTilePoints(tile);
56
+ });
57
+
58
+ export const selectCellIsValid = createSelector([selectConfig, selectCell], (config, cell) => {
59
+ if (!cell.hasTile()) {
60
+ return true;
61
+ }
62
+
63
+ return config.tiles.some((tile) => tile.character === cell.tile.character);
64
+ });
65
+
66
+ export const selectTranslations = createSelector([selectLocale], (locale) => i18n[locale]);
67
+
68
+ export const selectRemoveCellFilters = createSelector([selectSettings], (settings) => settings.removeCellFilters);
69
+
@@ -1,9 +1,9 @@
1
- import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2
- import { Game, Locale, ShowCoordinates } from '@scrabble-solver/types';
1
+ import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
2
+ import { type Game, type Locale, type ShowCoordinates } from '@scrabble-solver/types';
3
3
 
4
- import { AutoGroupTiles, InputMode } from 'types';
4
+ import type { AutoGroupTiles, InputMode, RemoveCellFilters } from 'types';
5
5
 
6
- import { settingsInitialState } from './settingsInitialState';
6
+ import { settingsInitialState } from './initialState';
7
7
 
8
8
  export const settingsSlice = createSlice({
9
9
  initialState: settingsInitialState,
@@ -34,6 +34,11 @@ export const settingsSlice = createSlice({
34
34
  return { ...state, showCoordinates };
35
35
  },
36
36
 
37
+ changeRemoveCellFilters: (state, action: PayloadAction<RemoveCellFilters>) => {
38
+ const removeCellFilters = action.payload;
39
+ return { ...state, removeCellFilters };
40
+ },
41
+
37
42
  init: (state, action: PayloadAction<Partial<Pick<typeof settingsInitialState, 'game' | 'locale'>>>) => {
38
43
  return { ...state, ...action.payload };
39
44
  },
@@ -0,0 +1,12 @@
1
+ import { type Game, type Locale, type ShowCoordinates } from '@scrabble-solver/types';
2
+
3
+ import type { AutoGroupTiles, InputMode, RemoveCellFilters } from 'types';
4
+
5
+ export interface SettingsState {
6
+ autoGroupTiles: AutoGroupTiles;
7
+ game: Game;
8
+ inputMode: InputMode;
9
+ locale: Locale;
10
+ showCoordinates: ShowCoordinates;
11
+ removeCellFilters: RemoveCellFilters;
12
+ }
@@ -0,0 +1,4 @@
1
+ export * from './initialState';
2
+ export * from './selectors';
3
+ export * from './slice';
4
+ export * from './types';
@@ -0,0 +1,12 @@
1
+ import { boardInitialState } from '../board';
2
+
3
+ import type { SolveState } from './types';
4
+
5
+ export const solveInitialState: SolveState = {
6
+ error: undefined,
7
+ isLoading: false,
8
+ lastSolvedParameters: {
9
+ board: boardInitialState,
10
+ characters: [],
11
+ },
12
+ };
@@ -0,0 +1,14 @@
1
+ import { createSelector } from '@reduxjs/toolkit';
2
+ import { isError } from '@scrabble-solver/types';
3
+
4
+ import type { RootState } from '../types';
5
+
6
+ export const selectSolve = (state: RootState) => state.solve;
7
+
8
+ export const selectSolveError = createSelector([selectSolve], (solve) => {
9
+ return isError(solve.error) ? solve.error : undefined;
10
+ });
11
+
12
+ export const selectSolveIsLoading = createSelector([selectSolve], (solve) => solve.isLoading);
13
+
14
+ export const selectLastSolvedParameters = createSelector([selectSolve], (solve) => solve.lastSolvedParameters);
@@ -1,7 +1,7 @@
1
- import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2
- import { Board } from '@scrabble-solver/types';
1
+ import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
2
+ import { type Board } from '@scrabble-solver/types';
3
3
 
4
- import { solveInitialState } from './solveInitialState';
4
+ import { solveInitialState } from './initialState';
5
5
 
6
6
  interface SolveParameters {
7
7
  board: Board;
@@ -0,0 +1,10 @@
1
+ import { type Board } from '@scrabble-solver/types';
2
+
3
+ export interface SolveState {
4
+ error: unknown;
5
+ isLoading: boolean;
6
+ lastSolvedParameters: {
7
+ board: Board;
8
+ characters: string[];
9
+ };
10
+ }
@@ -1,24 +1,22 @@
1
1
  import { configureStore } from '@reduxjs/toolkit';
2
2
  import reduxSaga from 'redux-saga';
3
3
 
4
+ import { boardSlice } from './board';
5
+ import { cellFiltersSlice } from './cellFilters';
6
+ import { dictionarySlice } from './dictionary';
7
+ import { rackSlice } from './rack';
8
+ import { resultsSlice } from './results';
4
9
  import { rootSaga } from './sagas';
5
- import {
6
- boardSlice,
7
- cellFilterSlice,
8
- dictionarySlice,
9
- rackSlice,
10
- resultsSlice,
11
- settingsSlice,
12
- solveSlice,
13
- verifySlice,
14
- } from './slices';
10
+ import { settingsSlice } from './settings';
11
+ import { solveSlice } from './solve';
12
+ import { verifySlice } from './verify';
15
13
 
16
14
  const sagaMiddleware = reduxSaga();
17
15
 
18
16
  export const store = configureStore({
19
17
  reducer: {
20
18
  board: boardSlice.reducer,
21
- cellFilter: cellFilterSlice.reducer,
19
+ cellFilters: cellFiltersSlice.reducer,
22
20
  dictionary: dictionarySlice.reducer,
23
21
  rack: rackSlice.reducer,
24
22
  results: resultsSlice.reducer,