procon_bypass_man-web 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,123 @@
1
+ import { Button, buttons } from "../types/button";
2
+ import { ButtonInLayer, ButtonsInLayer, Layers, ButtonsSettingType } from "../types/buttons_setting_type";
3
+ import { ButtonStateDiff } from "./button_state_diff";
4
+
5
+ const makeLayer = () => {
6
+ const defaultLayer = buttons.reduce((acc, item) => { acc[item] = { open: false }; return acc; }, {} as ButtonsInLayer);
7
+ const upLayer = Object.assign({}, defaultLayer);
8
+ const downLayer = Object.assign({}, defaultLayer);
9
+ const leftLayer = Object.assign({}, defaultLayer);
10
+ const rightLayer = Object.assign({}, defaultLayer);
11
+ return {
12
+ prefix_keys_for_changing_layer: [] as Array<Button>,
13
+ layers: {
14
+ up: Object.assign({}, upLayer),
15
+ down: Object.assign({}, downLayer),
16
+ left: Object.assign({}, leftLayer),
17
+ right: Object.assign({}, rightLayer),
18
+ installed_macros: {},
19
+ installed_modes: {},
20
+ }
21
+ };
22
+ };
23
+
24
+ describe('全部ブランクの時', () => {
25
+ it('[]を返す', () => {
26
+ const before = Object.assign({}, makeLayer());
27
+ const after = Object.assign({}, makeLayer());
28
+
29
+ const actual = ButtonStateDiff({ before: before, after: after })
30
+ expect(actual).toStrictEqual([])
31
+ })
32
+ })
33
+
34
+ describe('keyで差分がある時', () => {
35
+ it('値を返す', () => {
36
+ const before = Object.assign({}, makeLayer());
37
+ const after = Object.assign({}, makeLayer());
38
+
39
+ before.prefix_keys_for_changing_layer = ["a"];
40
+ after.prefix_keys_for_changing_layer = ["y", "x"];
41
+
42
+ const actual = ButtonStateDiff({ before: before, after: after })
43
+ expect(actual).toStrictEqual(["key prefixは a => y,x になります"])
44
+ })
45
+ })
46
+
47
+ describe('layersで差分がある時', () => {
48
+ it('値を返す', () => {
49
+ const before = Object.assign({}, makeLayer());
50
+ const after = Object.assign({}, makeLayer());
51
+
52
+ before.prefix_keys_for_changing_layer = ["a"];
53
+ after.prefix_keys_for_changing_layer = ["a"];
54
+ before.layers.up.a = { flip: { if_pressed: ["y"], enable: true }, open: true }
55
+ before.layers.down.a = { flip: { if_pressed: ["y"], enable: true }, open: true }
56
+ after.layers.up.a = { flip: { if_pressed: ["a"], enable: false }, open: true }
57
+
58
+ const actual = ButtonStateDiff({ before: before, after: after })
59
+ expect(actual).toStrictEqual([
60
+ "layer up の a を変更しました",
61
+ "layer down の a を変更しました",
62
+ ])
63
+ })
64
+ })
65
+
66
+ describe('installed_macrosで差分がある時', () => {
67
+ it('値を返す', () => {
68
+ const before = Object.assign({}, makeLayer());
69
+ const after = Object.assign({}, makeLayer());
70
+
71
+ before.layers.installed_macros = { a: true }
72
+
73
+ const actual = ButtonStateDiff({ before: before, after: after })
74
+ expect(actual).toStrictEqual([
75
+ "インストール可能なマクロを変更しました"
76
+ ])
77
+ })
78
+ })
79
+
80
+ describe('layer.x.macroで差分がある時', () => {
81
+ it('値を返す', () => {
82
+ const before = Object.assign({}, makeLayer());
83
+ const after = Object.assign({}, makeLayer());
84
+
85
+ before.layers.up.macro = { a: [] }
86
+ after.layers.up.macro = { a: ["b"] }
87
+
88
+ const actual = ButtonStateDiff({ before: before, after: after })
89
+ expect(actual).toStrictEqual([
90
+ "layer up の マクロ を変更しました"
91
+ ])
92
+ })
93
+ })
94
+
95
+ describe('installed_modesで差分がある時', () => {
96
+ it('値を返す', () => {
97
+ const before = Object.assign({}, makeLayer());
98
+ const after = Object.assign({}, makeLayer());
99
+
100
+ before.layers.installed_modes = { b: true }
101
+
102
+ const actual = ButtonStateDiff({ before: before, after: after })
103
+ expect(actual).toStrictEqual([
104
+ "インストール可能なモードを変更しました"
105
+ ])
106
+ })
107
+ })
108
+
109
+ describe('layer.x.modeで差分がある時', () => {
110
+ it('値を返す', () => {
111
+ const before = Object.assign({}, makeLayer());
112
+ const after = Object.assign({}, makeLayer());
113
+
114
+ before.layers.up.mode = {}
115
+ after.layers.up.mode = { a: true }
116
+
117
+ const actual = ButtonStateDiff({ before: before, after: after })
118
+ expect(actual).toStrictEqual([
119
+ "layer up の モード を変更しました"
120
+ ])
121
+ })
122
+ })
123
+
@@ -0,0 +1,63 @@
1
+ import { Button, buttons } from "../types/button";
2
+ import { ButtonInLayer, ButtonsInLayer, Layers, ButtonsSettingType, Macro, ModeTable } from "../types/buttons_setting_type";
3
+ import { LayerKey, layerKeys } from "../types/layer_key";
4
+ import { diff } from 'deep-object-diff';
5
+
6
+ type Props = {
7
+ before: ButtonsSettingType;
8
+ after: ButtonsSettingType;
9
+ };
10
+
11
+ export const ButtonStateDiff = ({ before, after }: Props): Array<string> => {
12
+ const changes = [] as Array<string>;
13
+
14
+ if(!before || !after || !before.layers) { return changes; };
15
+
16
+ if(before.layers.installed_macros && after.layers.installed_macros) {
17
+ const installedMacrosDiffResult = diff(before.layers.installed_macros, after.layers.installed_macros);
18
+ if(Object.keys(installedMacrosDiffResult || []).length > 0) {
19
+ changes.push("インストール可能なマクロを変更しました")
20
+ }
21
+ }
22
+
23
+ if(before.layers.installed_modes && after.layers.installed_modes) {
24
+ const installedModesDiffResult = diff(before.layers.installed_modes, after.layers.installed_modes);
25
+ if(Object.keys(installedModesDiffResult || []).length > 0) {
26
+ changes.push("インストール可能なモードを変更しました")
27
+ }
28
+ }
29
+
30
+ if(before.prefix_keys_for_changing_layer && after.prefix_keys_for_changing_layer) {
31
+ if(before.prefix_keys_for_changing_layer.toString() === after.prefix_keys_for_changing_layer.toString()) {
32
+ // no-op
33
+ } else {
34
+ changes.push(`key prefixは ${before.prefix_keys_for_changing_layer} => ${after.prefix_keys_for_changing_layer} になります`)
35
+ }
36
+ }
37
+
38
+ layerKeys.forEach((layerKey) => {
39
+ const beforeMacro = before.layers[layerKey].macro || {} as Macro
40
+ const afterMacro = after.layers[layerKey].macro || {} as Macro
41
+ const macroDiffResult = diff(beforeMacro, afterMacro);
42
+ if(Object.keys(macroDiffResult || []).length > 0) {
43
+ changes.push(`layer ${layerKey} の マクロ を変更しました`)
44
+ }
45
+
46
+ const beforeMode = before.layers[layerKey].mode || {} as ModeTable
47
+ const afterMode = after.layers[layerKey].mode || {} as ModeTable
48
+ const modeDiffResult = diff(beforeMode, afterMode);
49
+ if(Object.keys(modeDiffResult || []).length > 0) {
50
+ changes.push(`layer ${layerKey} の モード を変更しました`)
51
+ }
52
+
53
+ buttons.forEach((b) => {
54
+ // どの項目が何に変化したかを出力したかったがめんどくさすぎたのでやめた
55
+ const diffResult = diff(before.layers[layerKey as LayerKey][b], after.layers[layerKey as LayerKey][b]);
56
+ if(Object.keys(diffResult || []).length > 0) {
57
+ changes.push(`layer ${layerKey} の ${b} を変更しました`)
58
+ }
59
+ })
60
+ })
61
+
62
+ return changes;
63
+ }
@@ -0,0 +1,185 @@
1
+ import { ButtonsSettingConverter } from "./buttons_setting_converter";
2
+ import { Button, buttons } from "../types/button";
3
+ import { ButtonsInLayer } from "../types/buttons_setting_type";
4
+ import yaml from "js-yaml";
5
+ import _ from 'lodash';
6
+
7
+ describe('値があるとき', () => {
8
+ const prefixKeys = ["y", "l"] as Array<Button>;
9
+ const defaultLayer = buttons.reduce((acc, item) => { acc[item] = { open: false }; return acc; }, {} as ButtonsInLayer);
10
+ const upLayer = _.cloneDeep(defaultLayer);
11
+ const downLayer = _.cloneDeep(defaultLayer);
12
+ const leftLayer = _.cloneDeep(defaultLayer);
13
+ const rightLayer = _.cloneDeep(defaultLayer);
14
+ upLayer.a = { flip: { if_pressed: [], enable: true, force_neutral: ["y"] }, open: true }
15
+ upLayer.b = { flip: { if_pressed: ["a"], enable: true, force_neutral: ["y"] }, open: true }
16
+ upLayer.x = { flip: { if_pressed: ["a"], enable: false, force_neutral: ["y"] }, open: true }
17
+ upLayer.y = { flip: { if_pressed: [], enable: false, force_neutral: [] }, open: true }
18
+ downLayer.a = { flip: { if_pressed: ["a", "b"], enable: true, force_neutral: ["y", "x"] }, open: true }
19
+ leftLayer.a = { remap: { to: ["b", "y"] }, open: true }
20
+ const layers = {
21
+ up: upLayer,
22
+ down: downLayer,
23
+ left: leftLayer,
24
+ right: rightLayer,
25
+ };
26
+
27
+ it('validなyamlであること', () => {
28
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
29
+ yaml.load(actual)
30
+ })
31
+
32
+ it('定義が出力されること', () => {
33
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
34
+ const expected = `version: 1.0
35
+ setting: |-
36
+
37
+
38
+ prefix_keys_for_changing_layer %i(y l)
39
+
40
+ layer :up do
41
+ flip :a, force_neutral: %i(y)
42
+ flip :b, if_pressed: %i(a), force_neutral: %i(y)
43
+
44
+ end
45
+ layer :right do
46
+
47
+ end
48
+ layer :down do
49
+ flip :a, if_pressed: %i(a b), force_neutral: %i(y x)
50
+
51
+ end
52
+ layer :left do
53
+ remap :a, to: %i(b y)
54
+
55
+ end`;
56
+ expect(actual).toBe(expected);
57
+ })
58
+ })
59
+
60
+ describe('macroがあるとき', () => {
61
+ const prefixKeys = ["y", "l"] as Array<Button>;
62
+ const defaultLayer = buttons.reduce((acc, item) => { acc[item] = { open: false }; return acc; }, {} as ButtonsInLayer);
63
+ const upLayer = _.cloneDeep(defaultLayer);
64
+ upLayer.macro = { "ProconBypassMan::Splatoon2::Macro::FastReturn": ["y", "l"] }
65
+ const downLayer = _.cloneDeep(defaultLayer);
66
+ const leftLayer = _.cloneDeep(defaultLayer);
67
+ const rightLayer = _.cloneDeep(defaultLayer);
68
+ const layers = {
69
+ up: upLayer,
70
+ down: downLayer,
71
+ left: leftLayer,
72
+ right: rightLayer,
73
+ installed_macros: {
74
+ "ProconBypassMan::Splatoon2::Macro::FastReturn": true,
75
+ "ProconBypassMan::Sumabura::Macro::Foo": true,
76
+ "HOGE": false,
77
+ }
78
+ };
79
+
80
+ it('installed_macroを出力すること', () => {
81
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
82
+ const expected = `version: 1.0
83
+ setting: |-
84
+ install_macro_plugin ProconBypassMan::Splatoon2::Macro::FastReturn
85
+ install_macro_plugin ProconBypassMan::Sumabura::Macro::Foo
86
+
87
+ prefix_keys_for_changing_layer %i(y l)
88
+
89
+ layer :up do
90
+ macro ProconBypassMan::Splatoon2::Macro::FastReturn, if_pressed: %i(y l)
91
+ end
92
+ layer :right do
93
+
94
+ end
95
+ layer :down do
96
+
97
+ end
98
+ layer :left do
99
+
100
+ end`;
101
+ expect(actual).toBe(expected);
102
+ })
103
+ })
104
+
105
+ describe('modeがあるとき', () => {
106
+ const prefixKeys = ["y", "l"] as Array<Button>;
107
+ const defaultLayer = buttons.reduce((acc, item) => { acc[item] = { open: false }; return acc; }, {} as ButtonsInLayer);
108
+ const upLayer = _.cloneDeep(defaultLayer);
109
+ upLayer.mode = { "ProconBypassMan::Splatoon2::Mode::Guruguru": true }
110
+ const downLayer = _.cloneDeep(defaultLayer);
111
+ const leftLayer = _.cloneDeep(defaultLayer);
112
+ const rightLayer = _.cloneDeep(defaultLayer);
113
+ const layers = {
114
+ up: upLayer,
115
+ down: downLayer,
116
+ left: leftLayer,
117
+ right: rightLayer,
118
+ installed_modes: {
119
+ "ProconBypassMan::Splatoon2::Mode::Guruguru": true,
120
+ "unknown": false,
121
+ }
122
+ };
123
+
124
+ it('installed_modeを出力すること', () => {
125
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
126
+ const expected = `version: 1.0
127
+ setting: |-
128
+
129
+ install_mode_plugin ProconBypassMan::Splatoon2::Mode::Guruguru
130
+ prefix_keys_for_changing_layer %i(y l)
131
+
132
+ layer :up, mode: ProconBypassMan::Splatoon2::Mode::Guruguru do
133
+
134
+ end
135
+ layer :right do
136
+
137
+ end
138
+ layer :down do
139
+
140
+ end
141
+ layer :left do
142
+
143
+ end`;
144
+ expect(actual).toBe(expected);
145
+ })
146
+ })
147
+
148
+ describe('全部からのとき', () => {
149
+ const prefixKeys = ["y", "l"] as Array<Button>;
150
+ const defaultLayer = buttons.reduce((acc, item) => { acc[item] = { open: false }; return acc; }, {} as ButtonsInLayer);
151
+ const layers = {
152
+ up: defaultLayer,
153
+ down: defaultLayer,
154
+ left: defaultLayer,
155
+ right: defaultLayer,
156
+ };
157
+
158
+ it('validなyamlであること', () => {
159
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
160
+ yaml.load(actual)
161
+ })
162
+
163
+ it('全部空になること', () => {
164
+ const actual = ButtonsSettingConverter({ prefixKeys: prefixKeys, layers: layers })
165
+ const expected = `version: 1.0
166
+ setting: |-
167
+
168
+
169
+ prefix_keys_for_changing_layer %i(y l)
170
+
171
+ layer :up do
172
+
173
+ end
174
+ layer :right do
175
+
176
+ end
177
+ layer :down do
178
+
179
+ end
180
+ layer :left do
181
+
182
+ end`;
183
+ expect(actual).toBe(expected);
184
+ })
185
+ })
@@ -0,0 +1,107 @@
1
+ import { LayerKey, layerKeys } from "../types/layer_key";
2
+ import { Macro, Remap, Flip, Layers, ButtonsInLayer } from "../types/buttons_setting_type";
3
+ import { Button, buttons } from "../types/button";
4
+
5
+ type Props = {
6
+ prefixKeys: Array<Button>;
7
+ layers: Layers;
8
+ };
9
+ export const ButtonsSettingConverter = ({ prefixKeys, layers }: Props) => {
10
+ const layerBlock = (layerKey: LayerKey) => {
11
+ const modeTable = layers[layerKey].mode || {}
12
+ const currentMode = Object.keys(modeTable).toString();
13
+ if(currentMode === "disable" || currentMode === '') {
14
+ return `layer :${layerKey} do`;
15
+ } else {
16
+ const modeOption = `, mode: ${currentMode}`
17
+ return `layer :${layerKey}${modeOption} do`;
18
+ }
19
+ }
20
+ type defineButtonMethodProps = {
21
+ layer: ButtonsInLayer;
22
+ button: Button;
23
+ };
24
+ const buttons_with_macro = buttons
25
+ const createButtonMethod = ({ layer , button }: defineButtonMethodProps) => {
26
+ const flip = layer[button].flip;
27
+ const remap = layer[button].remap;
28
+ if(flip && flip.enable) {
29
+ if(!flip.if_pressed) { return }
30
+ if(flip.if_pressed.length === 0) {
31
+ return `flip :${button}${(flip.force_neutral || "") && `, force_neutral: %i(${flip.force_neutral?.join(" ")})`}`;
32
+ } else {
33
+ // ex) flip :a
34
+ // flip :a, if_pressed: [:b]
35
+ return `flip :${button}${(flip.if_pressed || "") && `, if_pressed: %i(${flip.if_pressed?.join(" ")})`}${(flip.force_neutral || "") && `, force_neutral: %i(${flip.force_neutral?.join(" ")})`}`;
36
+ }
37
+ } else { // flipとremapは共存できないのでelseにする
38
+ if(remap) {
39
+ return `remap :${button}${(remap.to || "") && `, to: %i(${remap.to.join(" ")})`}`;
40
+ }
41
+ }
42
+ return null;
43
+ }
44
+
45
+ const layerBlockIndent = " ";
46
+ const topLevelIndent = " ";
47
+ if(!layers.installed_macros) { layers.installed_macros = {} };
48
+ if(!layers.installed_modes) { layers.installed_modes = {} };
49
+ if(!layers.up.macro) { layers.up.macro = {} };
50
+ if(!layers.down.macro) { layers.down.macro = {} };
51
+ if(!layers.right.macro) { layers.right.macro = {} };
52
+ if(!layers.left.macro) { layers.left.macro = {} };
53
+ const installedMacroNames = Object.entries(layers.installed_macros).filter((k, i) => k[1]).map(v => v[0]);
54
+ const installedModeNames = Object.entries(layers.installed_modes).filter((k, i) => k[1]).map(v => v[0]);
55
+
56
+ const expandMacroInLayer = (macro: Macro) => {
57
+ return Object.entries(macro).map((m) => {
58
+ const name = m[0] as string;
59
+ const ifPressed = m[1] as Array<Button>;
60
+ return `${layerBlockIndent}macro ${name}, if_pressed: %i(${ifPressed.join(" ")})` }
61
+ )
62
+ };
63
+
64
+ return(
65
+ `version: 1.0
66
+ setting: |-
67
+ ${installedMacroNames.map((name) => `${topLevelIndent}install_macro_plugin ${name}`).join("\n")}
68
+ ${installedModeNames.map((name) => `${topLevelIndent}install_mode_plugin ${name}`).join("\n")}
69
+ prefix_keys_for_changing_layer %i(${prefixKeys.join(" ")})
70
+
71
+ ${
72
+ buttons.reduce((a, b) => {
73
+ const m = createButtonMethod({ layer: layers.up, button: b })
74
+ if(m) { a = a + `\n${layerBlockIndent}` + m }
75
+ return a;
76
+ }, layerBlock("up"))
77
+ }
78
+ ${expandMacroInLayer(layers.up.macro)}
79
+ end
80
+ ${
81
+ buttons.reduce((a, b) => {
82
+ const m = createButtonMethod({ layer: layers.right, button: b })
83
+ if(m) { a = a + `\n${layerBlockIndent}` + m }
84
+ return a;
85
+ }, layerBlock("right"))
86
+ }
87
+ ${expandMacroInLayer(layers.down.macro)}
88
+ end
89
+ ${
90
+ buttons.reduce((a, b) => {
91
+ const m = createButtonMethod({ layer: layers.down, button: b })
92
+ if(m) { a = a + `\n${layerBlockIndent}` + m }
93
+ return a;
94
+ }, layerBlock("down"))
95
+ }
96
+ ${expandMacroInLayer(layers.down.macro)}
97
+ end
98
+ ${
99
+ buttons.reduce((a, b) => {
100
+ const m = createButtonMethod({ layer: layers.left, button: b })
101
+ if(m) { a = a + `\n${layerBlockIndent}` + m }
102
+ return a;
103
+ }, layerBlock("left"))
104
+ }
105
+ ${expandMacroInLayer(layers.left.macro)}
106
+ end`)
107
+ }