empathy: Fix judging interface to properly merge two entire groups
authorCarl Worth <cworth@cworth.org>
Fri, 12 Jun 2020 00:53:20 +0000 (17:53 -0700)
committerCarl Worth <cworth@cworth.org>
Fri, 12 Jun 2020 00:53:20 +0000 (17:53 -0700)
Previously, when selecting an item that was a part of a larger group,
it could get pulled out of that group to be merged with the other
selected item. This was absolutely bewildering to players.

Instead, the right thing is to entirely merge the two groups to which
each of the two selected items belong. That's what we do here, and
using Set objects rather than simply arrays as we had before.

empathy/empathy.jsx

index 3d7034527c0c8bd49dc8fea14dcf14af96b5520e..dee5a677ff47da93c0307b84af7cec0be8399db6 100644 (file)
@@ -349,8 +349,14 @@ class Ambiguities extends React.PureComponent {
   constructor(props) {
     super(props);
 
+    const word_sets = props.words.map(word => {
+      const set = new Set();
+      set.add(word);
+      return set;
+    });
+
     this.state = {
-      word_groups: props.words.map(word => [word]),
+      word_sets: word_sets,
       submitted: false,
       selected: null
     };
@@ -359,7 +365,7 @@ class Ambiguities extends React.PureComponent {
   async handle_submit() {
     const response = await fetch_post_json(
       `judging/${this.props.prompt.id}`,{
-        word_groups: this.state.word_groups
+        word_groups: this.state.word_sets.map(set => Array.from(set))
       }
     );
 
@@ -382,35 +388,42 @@ class Ambiguities extends React.PureComponent {
   handle_click(word) {
     if (this.state.selected == word) {
       /* Second click on same word removes the word from the group. */
-      const new_groups = this.state.word_groups.filter(
-        group => (! group.includes(this.state.selected)) || (group.length > 1)).map(
-          group => {
-            return group.filter(w => w !== this.state.selected);
-          }
-        );
+      const idx = this.state.word_sets.findIndex(s => s.has(word));
+      const set = this.state.word_sets[idx];
+      if (set.size === 1)
+        return;
+      const new_set = new Set([...set].filter(w => w !== word));
       this.setState({
         selected: null,
-        word_groups: [...new_groups, [word]]
+        word_sets: [...this.state.word_sets.slice(0, idx),
+                    new_set,
+                    new Set().add(word),
+                    ...this.state.word_sets.slice(idx+1)]
       });
     } else if (this.state.selected) {
       /* Click of a second word groups the two together. */
-      const new_groups = this.state.word_groups.filter(
-        group => (! group.includes(word)) || (group.length > 1)).map(
-          group => {
-            if (group.includes(this.state.selected)) {
-              if (! group.includes(word))
-                return [...group, word];
-              else
-                return group;
-            } else {
-              return group.filter(w => w !== word);
-            }
-          }
-        );
-      this.setState({
-        selected: null,
-        word_groups: new_groups
-      });
+      const idx1 = this.state.word_sets.findIndex(s => s.has(this.state.selected));
+      const idx2 = this.state.word_sets.findIndex(s => s.has(word));
+      const set1 = this.state.word_sets[idx1];
+      const set2 = this.state.word_sets[idx2];
+      const new_set = new Set([...set2, ...set1]);
+      if (idx1 < idx2) {
+        this.setState({
+          selected: null,
+          word_sets: [...this.state.word_sets.slice(0, idx1),
+                      ...this.state.word_sets.slice(idx1 + 1, idx2),
+                      new_set,
+                      ...this.state.word_sets.slice(idx2 + 1)]
+        });
+      } else {
+        this.setState({
+          selected: null,
+          word_sets: [...this.state.word_sets.slice(0, idx2),
+                      new_set,
+                      ...this.state.word_sets.slice(idx2 + 1, idx1),
+                      ...this.state.word_sets.slice(idx1 + 1)]
+        });
+      }
     } else {
       /* First click of a word selects it. */
       this.setState({
@@ -443,24 +456,24 @@ class Ambiguities extends React.PureComponent {
           what goes around comes around, so it's best to be generous when
           judging.
         </p>
-        {this.state.word_groups.map(word_group => {
+        {this.state.word_sets.map(set => {
           return (
             <div
               className="ambiguity-group"
-              key={word_group[0]}
+              key={Array.from(set)[0]}
             >
-            {word_group.map(word => {
-              return (
-                <button
-                className={this.state.selected === word ?
-                           btn_selected_class : btn_class }
-                key={word}
-                onClick={() => this.handle_click(word)}
+              {Array.from(set).map(word => {
+                return (
+                  <button
+                    className={this.state.selected === word ?
+                               btn_selected_class : btn_class }
+                    key={word}
+                    onClick={() => this.handle_click(word)}
                   >
-                {word}
-                </button>
-              );
-            })}
+                    {word}
+                  </button>
+                );
+              })}
             </div>
           );
         })}