procon_bypass_man-web 0.1.0

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/.babelrc +6 -0
  3. data/.circleci/config.yml +73 -0
  4. data/.gitignore +12 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +26 -0
  7. data/CHANGELOG.md +3 -0
  8. data/Gemfile +18 -0
  9. data/Gemfile.lock +97 -0
  10. data/LICENSE.txt +21 -0
  11. data/Procfile +2 -0
  12. data/README.md +43 -0
  13. data/Rakefile +4 -0
  14. data/bin/console +15 -0
  15. data/bin/pbm_web +7 -0
  16. data/bin/setup +8 -0
  17. data/jest.config.ts +194 -0
  18. data/lib/procon_bypass_man/web.rb +20 -0
  19. data/lib/procon_bypass_man/web/db.rb +33 -0
  20. data/lib/procon_bypass_man/web/migration/001_create_settings_table.sql +4 -0
  21. data/lib/procon_bypass_man/web/models/base_model.rb +47 -0
  22. data/lib/procon_bypass_man/web/models/setting.rb +22 -0
  23. data/lib/procon_bypass_man/web/public/bundle.js +2 -0
  24. data/lib/procon_bypass_man/web/public/bundle.js.LICENSE.txt +57 -0
  25. data/lib/procon_bypass_man/web/public/index.html +1 -0
  26. data/lib/procon_bypass_man/web/server.rb +139 -0
  27. data/lib/procon_bypass_man/web/setting_parser.rb +190 -0
  28. data/lib/procon_bypass_man/web/storage.rb +25 -0
  29. data/lib/procon_bypass_man/web/version.rb +7 -0
  30. data/package.json +48 -0
  31. data/procon_bypass_man-web.gemspec +36 -0
  32. data/src/app.tsx +5 -0
  33. data/src/components/button_setting.tsx +142 -0
  34. data/src/components/buttons_modal.tsx +110 -0
  35. data/src/components/buttons_setting.tsx +67 -0
  36. data/src/components/installable_macros.tsx +58 -0
  37. data/src/components/installable_modes.tsx +57 -0
  38. data/src/components/macro_settings.tsx +85 -0
  39. data/src/components/mode_settings.tsx +62 -0
  40. data/src/contexts/buttons_setting.ts +2 -0
  41. data/src/index.html +11 -0
  42. data/src/lib/button_state.test.ts +110 -0
  43. data/src/lib/button_state.ts +52 -0
  44. data/src/lib/button_state_diff.test.ts +123 -0
  45. data/src/lib/button_state_diff.ts +63 -0
  46. data/src/lib/buttons_setting_converter.test.ts +185 -0
  47. data/src/lib/buttons_setting_converter.ts +107 -0
  48. data/src/lib/http_client.ts +93 -0
  49. data/src/pages/bpm_page.tsx +92 -0
  50. data/src/pages/buttons_setting_page.tsx +281 -0
  51. data/src/pages/global_setting_page.tsx +83 -0
  52. data/src/pages/home.tsx +17 -0
  53. data/src/pages/recoding_mode_page.tsx +15 -0
  54. data/src/pages/top.tsx +107 -0
  55. data/src/reducers/layer_reducer.ts +120 -0
  56. data/src/types/button.ts +2 -0
  57. data/src/types/buttons_setting_type.ts +63 -0
  58. data/src/types/layer_key.ts +2 -0
  59. data/src/types/pbm_stats.ts +1 -0
  60. data/src/types/plugin.ts +43 -0
  61. data/tmp/.keep +0 -0
  62. data/tsconfig.json +75 -0
  63. data/webpack.config.js +56 -0
  64. data/yarn.lock +6815 -0
  65. metadata +150 -0
@@ -0,0 +1,83 @@
1
+ /** @jsx jsx */
2
+
3
+ import { jsx, css } from '@emotion/react'
4
+ import React, { useState, useEffect } from "react";
5
+ import axios from 'axios';
6
+
7
+ import { HttpClient } from "../lib/http_client";
8
+
9
+ type Prop = {
10
+ };
11
+ const httpClient = new HttpClient();
12
+
13
+ export const GlobalSetting = ({}:Prop) => {
14
+ const [dirPath, setDirPath] = useState("");
15
+ const [settingPath, setSettingPath] = useState("");
16
+ const [serverResponseMessage, setServerResponseMessage] = useState("");
17
+
18
+ const inputStyle = css`
19
+ width: 400px;
20
+ `
21
+
22
+ useEffect(() => {
23
+ httpClient.getDirPath()
24
+ .then(function (response) {
25
+ setDirPath(response.data.root_path);
26
+ })
27
+ .catch(function (error) {
28
+ setServerResponseMessage("サーバとの通信に失敗しました");
29
+ console.log(error);
30
+ })
31
+ httpClient.getSettingPath()
32
+ .then(function (response) {
33
+ setSettingPath(response.data.setting_path);
34
+ })
35
+ .catch(function (error) {
36
+ console.log(error);
37
+ })
38
+ }, [])
39
+
40
+ const handleDirChange = (e: React.ChangeEvent<HTMLInputElement>) => {
41
+ setDirPath(e.target.value);
42
+ };
43
+
44
+ const handleDirSubmit = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
45
+ if (!(e.target instanceof HTMLInputElement)) {
46
+ return;
47
+ }
48
+ httpClient.postDirPath(dirPath);
49
+ setServerResponseMessage("PBMのディレクトリパスの更新に成功しました");
50
+ };
51
+
52
+ const handleSettingChange = (e: React.ChangeEvent<HTMLInputElement>) => {
53
+ setSettingPath(e.target.value);
54
+ };
55
+
56
+ const handleSettingSubmit = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
57
+ if (!(e.target instanceof HTMLInputElement)) {
58
+ return;
59
+ }
60
+ httpClient.postSettingPath(settingPath);
61
+ setServerResponseMessage("PBMの設定ファイルパスの更新に成功しました");
62
+ };
63
+
64
+ return (
65
+ <>
66
+ <h2>設定</h2>
67
+ <div>
68
+ {serverResponseMessage}
69
+ </div>
70
+ <label>PBMのディレクトリパス:
71
+ <input type="text" css={inputStyle} value={dirPath} onChange={handleDirChange} />
72
+ </label>
73
+ <input type="submit" value="更新する" onClick={handleDirSubmit} />
74
+ <hr />
75
+
76
+ <label>PBMの設定ファイルパス:
77
+ <input type="text" css={inputStyle} value={settingPath || "未設定"} onChange={handleSettingChange} />
78
+ </label>
79
+ <input type="submit" value="更新する" onClick={handleSettingSubmit} />
80
+ <hr />
81
+ </>
82
+ )
83
+ }
@@ -0,0 +1,17 @@
1
+ /** @jsx jsx */
2
+
3
+ import { jsx, css } from '@emotion/react'
4
+ import React, { useState, useEffect } from "react";
5
+
6
+ export const Home = () => {
7
+ return (
8
+ <>
9
+ <div css={css`margin-top: 30px`}>
10
+ ソースコード: <a href="https://github.com/splaplapla/procon_bypass_man-web">
11
+ https://github.com/splaplapla/procon_bypass_man-web
12
+ </a>
13
+ </div>
14
+ </>
15
+ )
16
+ }
17
+
@@ -0,0 +1,15 @@
1
+ /** @jsx jsx */
2
+
3
+ import { jsx, css } from '@emotion/react'
4
+ import React, { useState, useEffect } from "react";
5
+ import { HttpClient } from "../lib/http_client";
6
+
7
+ const httpClient = new HttpClient();
8
+
9
+ export const RecodingModePage = () => {
10
+ return (
11
+ <>
12
+ <div></div>
13
+ </>
14
+ )
15
+ }
data/src/pages/top.tsx ADDED
@@ -0,0 +1,107 @@
1
+ /** @jsx jsx */
2
+
3
+ import { jsx, css } from '@emotion/react'
4
+ import React, { useState, useReducer } from "react";
5
+ import {
6
+ BrowserRouter as Router,
7
+ Switch,
8
+ Route,
9
+ Link
10
+ } from "react-router-dom";
11
+ import { GlobalSetting } from "./global_setting_page";
12
+ import { BpmPage } from "./bpm_page";
13
+ import { Home } from "./home";
14
+ import { ButtonsSettingPage } from "./buttons_setting_page";
15
+ import { RecodingModePage } from "./recoding_mode_page";
16
+ import { ButtonsSettingContext } from "./../contexts/buttons_setting";
17
+ import { ButtonsInLayer, Layers, ButtonsSettingType } from "../types/buttons_setting_type";
18
+ import { buttons, Button } from "../types/button";
19
+ import { LayerReducer } from "../reducers/layer_reducer";
20
+
21
+ const ButtonsSettingProfile: React.FC = ({children}) => {
22
+ const initLayers: Layers = {
23
+ up: buttons.reduce((a, i) => { a[i] = { open: false }; return a }, {} as ButtonsInLayer),
24
+ right: buttons.reduce((a, i) => { a[i] = { open: false }; return a }, {} as ButtonsInLayer),
25
+ down: buttons.reduce((a, i) => { a[i] = { open: false }; return a }, {} as ButtonsInLayer),
26
+ left: buttons.reduce((a, i) => { a[i] = { open: false }; return a }, {} as ButtonsInLayer),
27
+ installed_macros: {},
28
+ installed_modes: {},
29
+ }
30
+ const [prefixKeys, setPrefixKeys] = useState([]);
31
+ const [loaded, setLoaded] = useState(false);
32
+ const [layers, layersDispatch] = useReducer(LayerReducer, initLayers as Layers);
33
+ const value = {
34
+ loaded,
35
+ setLoaded,
36
+ layers,
37
+ prefixKeys,
38
+ setPrefixKeys,
39
+ layersDispatch,
40
+ }
41
+ return (
42
+ <ButtonsSettingContext.Provider value={value}>
43
+ {children}
44
+ </ButtonsSettingContext.Provider>
45
+ )
46
+ }
47
+
48
+ export const Top: React.FC = () => {
49
+ const menuStyle = css`
50
+ ul {
51
+ list-style: none;
52
+ display:flex;
53
+ margin: 0;
54
+ padding: 0;
55
+ li {
56
+ padding-right: 3px;
57
+ display: block;
58
+ a {
59
+ text-decoration: none;
60
+ display:block;
61
+ padding:10px;
62
+ background-color: #333;
63
+ color: white;
64
+ }
65
+ }
66
+ }
67
+ `;
68
+
69
+ return (
70
+ <>
71
+ <Router>
72
+ <div>
73
+ <nav css={menuStyle}>
74
+ <ul>
75
+ <li>
76
+ <Link to="/">home</Link>
77
+ </li>
78
+ <li>
79
+ <Link to="/setting">設定</Link>
80
+ </li>
81
+ <li>
82
+ <Link to="/buttons_setting">ボタン設定</Link>
83
+ </li>
84
+ </ul>
85
+ </nav>
86
+ <Switch>
87
+ <Route exact path="/" >
88
+ <Home />
89
+ </Route>
90
+ <Route path="/setting">
91
+ <GlobalSetting />
92
+ </Route>
93
+ <Route path="/pbm">
94
+ <BpmPage />
95
+ </Route>
96
+ <Route path="/buttons_setting">
97
+ <ButtonsSettingProfile><ButtonsSettingPage /></ButtonsSettingProfile>
98
+ </Route>
99
+ <Route path="/recoding_mode">
100
+ <RecodingModePage />
101
+ </Route>
102
+ </Switch>
103
+ </div>
104
+ </Router>
105
+ </>
106
+ );
107
+ };
@@ -0,0 +1,120 @@
1
+ import { buttons, Button } from "../types/button";
2
+ import { LayerKey } from "../types/layer_key";
3
+ import { Layers, Flip, Remap, Macro, StructMacro, ModeTable, StructMode } from "../types/buttons_setting_type";
4
+
5
+ export const disableFlipType = Symbol('disableFlip');
6
+ export const alwaysFlipType = Symbol('alwaysFlip');
7
+ export const flipIfPressedSelfType = Symbol('flipIfPressedSelf');
8
+ export const flipIfPressedSomeButtonsType = Symbol('flipIfPressedSomeButtons');
9
+ export const ignoreButtonsInFlipingType = Symbol('ignoreButtonsInFliping');
10
+ export const remapType = Symbol('remap');
11
+ export const openMenuType = Symbol('openMenu');
12
+ export const closeMenuType = Symbol('closeMenu');
13
+ export const applyMacroType = Symbol('applyMacro');
14
+ export const installMacroType = Symbol('installedMacro');
15
+ export const uninstallMacroType = Symbol('uninstalledMacro');
16
+ export const installModeType = Symbol('uninstalledMacro');
17
+ export const uninstallModeType = Symbol('a')
18
+ export const applyModeType = Symbol('b')
19
+
20
+
21
+ type ACTION_TYPE =
22
+ | { type: typeof disableFlipType, payload: { layerKey: LayerKey, button: Button } }
23
+ | { type: typeof alwaysFlipType, payload: { layerKey: LayerKey, button: Button } }
24
+ | { type: typeof flipIfPressedSelfType, payload: { layerKey: LayerKey, button: Button } }
25
+ | { type: typeof flipIfPressedSomeButtonsType, payload: { layerKey: LayerKey, button: Button, targetButtons: Array<Button> } }
26
+ | { type: typeof ignoreButtonsInFlipingType, payload: { layerKey: LayerKey, button: Button, targetButtons: Array<Button> } }
27
+ | { type: typeof remapType, payload: { layerKey: LayerKey, button: Button, targetButtons: Array<Button> } }
28
+ | { type: typeof openMenuType, payload: { layerKey: LayerKey, button: Button } }
29
+ | { type: typeof closeMenuType, payload: { layerKey: LayerKey, button: Button } }
30
+ | { type: typeof applyMacroType, payload: { layerKey: LayerKey, button: (Button | undefined), macro: StructMacro } }
31
+ | { type: typeof installMacroType, payload: { layerKey: (LayerKey | undefined), button: (Button | undefined), installed_macro: string } }
32
+ | { type: typeof uninstallMacroType, payload: { layerKey: (LayerKey | undefined), button: (Button | undefined), installed_macro: string } }
33
+ | { type: typeof installModeType, payload: { layerKey: (LayerKey | undefined), button: (Button | undefined), installed_mode: string } }
34
+ | { type: typeof uninstallModeType, payload: { layerKey: (LayerKey | undefined), button: (Button | undefined), installed_mode: string } }
35
+ | { type: typeof applyModeType, payload: { layerKey: LayerKey, button: (Button | undefined), mode: StructMode } }
36
+
37
+ export const LayerReducer = (layers: Layers, action: ACTION_TYPE) => {
38
+ const layerKey = action.payload.layerKey as LayerKey;
39
+ const button = action.payload.button as Button;
40
+
41
+ const flip = layerKey && button && layers[layerKey][button]?.flip || {} as Flip
42
+ const remap = layerKey && button && layers[layerKey][button]?.remap || {} as Remap
43
+
44
+ switch (action.type) {
45
+ case disableFlipType:
46
+ flip.enable = false;
47
+ layers[layerKey][button] = { flip: flip, open: true }
48
+ return { ...layers };
49
+ case alwaysFlipType:
50
+ flip.if_pressed = [];
51
+ flip.enable = true;
52
+ layers[layerKey][button] = { flip: flip, open: true }
53
+ return { ...layers };
54
+ case flipIfPressedSelfType:
55
+ flip.if_pressed = [button];
56
+ flip.enable = true;
57
+ layers[layerKey][button] = { flip: flip, open: true }
58
+ return { ...layers };
59
+ case flipIfPressedSomeButtonsType:
60
+ flip.if_pressed = action.payload.targetButtons;
61
+ flip.enable = true;
62
+ layers[layerKey][button] = { flip: flip, open: true }
63
+ return { ...layers };
64
+ case ignoreButtonsInFlipingType:
65
+ flip.force_neutral = action.payload.targetButtons;
66
+ layers[layerKey][button] = { flip: flip, open: true }
67
+ return { ...layers };
68
+ case remapType:
69
+ flip.enable = false;
70
+ remap.to = action.payload.targetButtons;
71
+ layers[layerKey][button] = { flip: flip, remap: remap, open: true }
72
+ return { ...layers };
73
+ case openMenuType:
74
+ flip.enable = false;
75
+ layers[layerKey][button] = { flip: flip, open: true };
76
+ return { ...layers };
77
+ case closeMenuType:
78
+ flip.enable = false;
79
+ layers[layerKey][button] = { flip: flip, open: false }
80
+ return { ...layers };
81
+ case applyMacroType:
82
+ const structMacro = action.payload.macro
83
+ if(!structMacro) { return { ...layers } };
84
+ const macroTable = layers[layerKey].macro as Macro || {} as Macro
85
+ macroTable[structMacro.name] = structMacro.if_pressed.sort()
86
+ layers[layerKey].macro = macroTable
87
+ return { ...layers };
88
+ case installMacroType:
89
+ const installedMacro = action.payload.installed_macro
90
+ const h = { ...layers }
91
+ h.installed_macros ||= {}
92
+ h.installed_macros[installedMacro] = true
93
+ return h;
94
+ case uninstallMacroType:
95
+ const unregisterInstalledMacro = action.payload.installed_macro
96
+ const hh = { ...layers }
97
+ hh.installed_macros ||= {}
98
+ hh.installed_macros[unregisterInstalledMacro] = false
99
+ return hh;
100
+ case installModeType:
101
+ const installedMode = action.payload.installed_mode
102
+ const l = { ...layers }
103
+ l.installed_modes ||= {}
104
+ l.installed_modes[installedMode] = true
105
+ return l;
106
+ case uninstallModeType:
107
+ const uninstallMode = action.payload.installed_mode
108
+ const uml = { ...layers }
109
+ uml.installed_modes ||= {}
110
+ uml.installed_modes[uninstallMode] = false
111
+ return uml;
112
+ case applyModeType:
113
+ const applyMode = action.payload.mode;
114
+ layers[layerKey].mode = { [applyMode.name]: true};
115
+ return { ...layers };
116
+ default:
117
+ console.log("一致しないaction typeです")
118
+ return { ...layers };
119
+ }
120
+ };
@@ -0,0 +1,2 @@
1
+ export const buttons = ["a", "b", "x", "y", "up", "right", "down", "left", "r", "l", "zr", "zl"] as const;
2
+ export type Button = typeof buttons[number];
@@ -0,0 +1,63 @@
1
+ import { Button } from "./button";
2
+ import { LayerKey } from "./layer_key";
3
+
4
+ export type Flip = {
5
+ if_pressed?: Array<Button>,
6
+ enable: Boolean,
7
+ force_neutral?: Array<Button>,
8
+ }
9
+
10
+ export type StructMacro = {
11
+ name: string,
12
+ if_pressed: Array<Button>,
13
+ }
14
+
15
+ export type StructMode = {
16
+ name: string,
17
+ }
18
+
19
+ export type Macro = {
20
+ [key in string]: Array<Button>;
21
+ }
22
+
23
+ export type ModeTable = {
24
+ [key in string]: boolean;
25
+ }
26
+
27
+ export type Remap = {
28
+ to: Array<Button>,
29
+ }
30
+
31
+ export type ButtonInLayer = {
32
+ flip?: Flip,
33
+ macro?: Macro, // deprecated
34
+ remap?: Remap,
35
+ open: boolean,
36
+ }
37
+
38
+ type _ButtonsInLayer = {
39
+ [key in Button] : ButtonInLayer;
40
+ }
41
+
42
+ export type ButtonsInLayer = _ButtonsInLayer & {
43
+ macro?: Macro; // TODO macroTableという名前にしたい
44
+ mode?: ModeTable;
45
+ };
46
+
47
+ export type InstalledPlugin = {
48
+ [key in string]: boolean;
49
+ }
50
+
51
+ export type Layers = {
52
+ up: ButtonsInLayer,
53
+ right: ButtonsInLayer,
54
+ down: ButtonsInLayer,
55
+ left: ButtonsInLayer,
56
+ installed_macros?: InstalledPlugin,
57
+ installed_modes?: InstalledPlugin,
58
+ }
59
+
60
+ export type ButtonsSettingType = {
61
+ prefix_keys_for_changing_layer: Array<Button>;
62
+ layers: Layers;
63
+ }
@@ -0,0 +1,2 @@
1
+ export const layerKeys = ["up", "right", "down", "left"] as const;
2
+ export type LayerKey = typeof layerKeys[number];
@@ -0,0 +1 @@
1
+ export type PbmStats = "stopped" | "running" | "unknown" | "waiting" | "error";
@@ -0,0 +1,43 @@
1
+ export type PluginBody = {
2
+ display_name: string;
3
+ class_namespace: string;
4
+ }
5
+
6
+ export type Plugin = {
7
+ [key in string] : {
8
+ modes: Array<PluginBody>;
9
+ macros: Array<PluginBody>;
10
+ }
11
+ }
12
+
13
+ // plugins.
14
+ export const AvailablePlugins = [
15
+ {
16
+ splatoon2: {
17
+ modes: [
18
+ { display_name: "splatoon2.guruguru", class_namespace: "ProconBypassMan::Splatoon2::Mode::Guruguru" },
19
+ ],
20
+ macros: [
21
+ { display_name: "splatoon2.fast_return", class_namespace: "ProconBypassMan::Splatoon2::Macro::FastReturn" },
22
+ ],
23
+ }
24
+ } as Plugin,
25
+ ]
26
+
27
+ export const MacroNameMap = AvailablePlugins.reduce((hash, item: Plugin) => {
28
+ for (var [name, plugin] of Object.entries(item)) {
29
+ plugin.macros.forEach((macro: PluginBody) => {
30
+ hash[macro.class_namespace] = macro.display_name
31
+ })
32
+ };
33
+ return hash;
34
+ }, {} as any)
35
+
36
+ export const ModeNameMap = AvailablePlugins.reduce((hash, item: Plugin) => {
37
+ for (var [name, plugin] of Object.entries(item)) {
38
+ plugin.modes.forEach((mode: PluginBody) => {
39
+ hash[mode.class_namespace] = mode.display_name
40
+ })
41
+ };
42
+ return hash;
43
+ }, {} as any)