File size: 12,777 Bytes
40a04d4
 
 
 
 
 
 
 
 
 
 
 
 
 
789d3cf
 
 
 
 
 
 
 
 
 
 
40a04d4
 
46adba2
40a04d4
87200e9
40a04d4
 
cb49766
87200e9
40a04d4
 
 
 
 
46adba2
 
87200e9
40a04d4
 
 
 
 
 
 
 
 
bca6484
87200e9
40a04d4
2ce66e6
40a04d4
 
 
 
cdf264c
63424e9
 
 
 
 
cb49766
63424e9
 
 
 
 
 
 
 
 
 
cb49766
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40a04d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cdf264c
40a04d4
 
 
 
 
 
789d3cf
40a04d4
 
 
 
789d3cf
 
 
 
 
40a04d4
 
 
 
 
 
 
 
2ce66e6
40a04d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cdf264c
40a04d4
 
 
 
 
 
 
 
 
2ce66e6
cdf264c
2ce66e6
 
 
 
 
 
 
 
 
 
 
 
 
40a04d4
 
 
cdf264c
40a04d4
87200e9
40a04d4
 
63424e9
 
 
 
cb49766
 
63424e9
 
 
 
 
 
 
cb49766
 
 
 
 
 
 
 
 
 
63424e9
 
 
 
 
 
 
 
cb49766
 
 
 
cdf264c
 
 
 
cb49766
 
63424e9
87200e9
40a04d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
{% extends 'base.html' %} {% block content %}
<div id="room-container">
  <div id="welcome-modal" class="modal">
    <div class="modal-content">
      <h3>Welcome!</h3>
      <p>
        Your display name for this chat session will be: 
        <span id="displayNameText" style="font-weight:bold;"></span>.
      </p>
      <div class="modal-buttons">
        <button class="modal-btn" id="welcomeOkBtn">OK</button>
      </div>
    </div>
  </div>

  <div id="timer-modal" class="modal">
    <div class="modal-content">
      <h3>Start a Timer</h3>
      <p><strong>Please start a timer for 20 minutes</strong>.  At the 20 minute mark, you may click the button "End Chat Session" to continue with the study.</p>
      <div class="modal-buttons">
        <button class="modal-btn" id="timerOkBtn">I have set a timer for 20 minutes.</button>
      </div>
    </div>
  </div>
  
  <h1 id="home-header">Chat Room</h1>
  <div id="room-subsection">
    <div class="topic-header-row">
      <div class="topic-header-info">
    	<h2 id="room-code-display">Topic: <span class="topic-title">{{ topic_info.title }}</span></h2>
      </div>
      <div class="topic-header-buttons">
        <button id="feedback-btn">Share Feedback and Report Bugs</button>
        <button id="end-exp-btn">End Chat Session</button>
        <button id="abort-exp-btn">Abort Experiment</button>
      </div>
    </div>
    <div id="end-modal" class="modal">
      <div class="modal-content">
    	<h3>Only Exit This Way After 20 Minutes of Participation in the Chatroom.</h3>
    	<p>This signals the end of the chat session of the experiment. You will be redirected to the post-survey.</p>
        <p>This button is only to be used at the end of your 20 minutes of participation in the chatroom. <strong>If you wish to exit the chat before finishing your 20 minutes, use the "Abort Experiment" button instead.</strong></p>
        <div class="modal-buttons">
          <button class="modal-btn" id="endYesBtn">Continue</button>
          <button class="modal-btn" id="endNoBtn">Cancel</button>
        </div>
      </div>
    </div>
    <div id="abort-modal" class="modal">
      <div class="modal-content">
        <h3>Are you sure you want to leave this experiment?</h3>
        <p><strong>If you finished your 20 minutes in the chatroom, do NOT exit via this button. Use the "End Chat Session" button instead.</strong></p>
        <p>By clicking yes, you will exit the experiment <strong>without</strong> completing the final survey.</p>
        <div class="modal-buttons">
          <button class="modal-btn" id="abortYesBtn">End early with code "C9V2XFDU"</button>
          <button class="modal-btn" id="abortNoBtn">Cancel</button>
        </div>
      </div>
    </div>

    <div id="feedback-modal" class="modal">
        <div class="modal-content">
            <h3>Feedback</h3>
            <form id="feedback-form">
                 <div class="feedback-col">
                   <label for="enter-feedback">Tell us if you noticed something confusing, unexpected, or not working.</label>
                    <textarea id="enter-feedback" name="feedback"></textarea>
                  </div>
                  <br>
                  <div class="modal-buttons">
                      <button class="modal-btn" id="submitFeedbackBtn" type="submit">Submit</button>
                      <button type="button" class="modal-btn" id="cancelFeedbackBtn">Cancel</button>
                  </div>
              </form>
          </div>
      </div>

    <div id="feedback-status-confirm" class="modal">
      <div class="modal-content">
        <p>Feedback submitted.</p>
        <div class="modal-buttons">
          <button class="modal-btn" id="cancelFeedbackStatusBtn">Ok</button>
        </div>
      </div>
    </div>
    <div id="feedback-status-fail" class="modal">
      <div class="modal-content">
        <p>Feedback failed to submit.</p>
        <div class="modal-buttons">
          <button class="modal-btn" id="cancelFeedbackStatusBtn2">Ok</button>
        </div>
      </div>
    </div>
  </div>
  <div id="chat-room-widget">
    <div id="msgs-container">
      <ul id="messages"></ul>
    </div>
    <div id="message-box">
      <textarea id="message-input" name="message" placeholder="Enter your message" rows="1"></textarea>
      <button type="submit" id="send-btn" onclick="sendMessage()">Send</button>
    </div>
  </div>
  <script type="text/javascript">
    // Push a state when entering the page
    history.pushState(null, "", location.href);
    window.addEventListener("popstate", function () {
      // Immediately push another state to prevent backward navigation
      history.pushState(null, "", location.href);
    });
    var socketio = io();
    const chatEnded = {{ ended | tojson }};
    const textarea = document.getElementById("message-input");
    if (chatEnded) {
      textarea.disabled = true;
      textarea.placeholder = "The chat has ended.";
      document.getElementById("send-btn").disabled = true;
      document.getElementById("end-exp-btn").disabled = true;
      document.getElementById("abort-exp-btn").disabled = true;
      document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
      if (socketio) {
        socketio.close();
      }
    }
    // Handler for the welcome modal
    let welcomeModal = document.getElementById("welcome-modal");
    let timerModal = document.getElementById("timer-modal");
    const displayNameText = document.getElementById("displayNameText");
    displayNameText.textContent = "{{ user }}";
    // Show the modal instantly when the page loads
    window.onload = function() {
      timerModal.style.display = "block";
    };
    //timer pop-up
    document.getElementById("timerOkBtn").onclick = function () {
      timerModal.style.display = "none";
      welcomeModal.style.display = "block";
    };
    // Close the modal on OK
    document.getElementById("welcomeOkBtn").onclick = function () {
      welcomeModal.style.display = "none";
    };
    // Creates the post-survey link (based on the bot names)
    const endpoint = "{{ url_for('post_survey') }}";
    const endpointQuitEarly = "https://app.prolific.com/submissions/complete?cc=C9V2XFDU";
    socketio.on("message", function (message) { createChatItem(message.message, message.sender) });
    function createChatItem(message, sender) {
      //autoscroll capabilities
      const container = document.getElementById("msgs-container");
      const shouldAutoScroll = isNearBottom(container);

      var messages = document.getElementById("messages");
      var content;
      if (sender === "") {
        content = `<p class="member-activity">${message}</p>`;
      } else {
        var senderIsUser = "{{user}}" === sender;
        content = `
          <li class="message-item ${senderIsUser ? "self-message-item" : "peer-message-item"}">
              <p>${message}</p>
              <small class="${senderIsUser ? "chat-user-sender" : "chat-sender"}">${sender}</small>
         </li>
      `;}
      messages.insertAdjacentHTML("beforeend", content);

      //autoscroll capabilities
      if (shouldAutoScroll) {
        smoothScrollToBottom(container);
      }
    }
    function sendMessage() {
      var msgInput = document.getElementById("message-input");
      if (msgInput.value === "") return;
      var msg = msgInput.value;
      socketio.emit("message", { message: msg });
      msgInput.value = "";
      msgInput.style.height = "auto";  // reset height
    }
    document.getElementById("message-input").addEventListener("keydown", function (event) {
      if (event.key === "Enter") {
        return
      	// disabling send message so user can type a newline without sending
        //event.preventDefault(); // prevent a newline or form submit
        //sendMessage(); // call the same function as the Send button
      }
    });
    textarea.addEventListener("input", () => {
      textarea.style.height = "auto";  // reset height
      textarea.style.overflowY = "hidden";  // start by hiding the scrollbar
      textarea.style.height = (textarea.scrollHeight + 8) + "px";  // set to fit content (+8 for bottom padding)
      // If we've hit the max height, allow scrolling
      if (textarea.scrollHeight > parseInt(getComputedStyle(textarea).maxHeight)) {
        textarea.style.overflowY = "auto";
      }
    });
    // Handler for the Experiment Ends confirmation pop-up
    const endModal = document.getElementById("end-modal");
    document.getElementById("end-exp-btn").onclick = function () {
      endModal.style.display = "block";
    };
    document.getElementById("endNoBtn").onclick = function () {
      endModal.style.display = "none";
    };
    document.getElementById("endYesBtn").onclick = function (e) {
      //block browser confirmation popup
      e.stopPropagation(); 
      // Redirect to ending survey
      window.open(endpoint, "_blank");
      endModal.style.display = "none";
      textarea.disabled = true;
      textarea.placeholder = "The chat has ended.";
      document.getElementById("send-btn").disabled = true;
      document.getElementById("end-exp-btn").disabled = true;
      document.getElementById("abort-exp-btn").disabled = true;
      document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
      if (socketio) {
        socketio.close();
      }
    };
    // Handler for the Abort Experiment confirmation pop-up
    let modal = document.getElementById("abort-modal");
    document.getElementById("abort-exp-btn").onclick = function () {
      modal.style.display = "block";
    };
    document.getElementById("abortNoBtn").onclick = function () {
      modal.style.display = "none";
    };
    
    document.getElementById("abortYesBtn").onclick = function (e) {
      //block browser confirmation popup
      e.stopPropagation(); 
      // Mark that user aborted and redirect to ending survey
      fetch("/abort", { method: "POST" })
        .then(() => {
          window.open(endpointQuitEarly, "_blank");
    	});
      modal.style.display = "none";
      textarea.disabled = true;
      textarea.placeholder = "The chat has ended.";
      document.getElementById("send-btn").disabled = true;
      document.getElementById("end-exp-btn").disabled = true;
      document.getElementById("abort-exp-btn").disabled = true;
      document.getElementById("feedback-btn").disabled = true; //since without socket io, it won't submit
      if (socketio) {
      	socketio.close();
      }
    };

    //handler for feedback modal popup
    let feedbackModal = document.getElementById("feedback-modal");
    let form = document.getElementById("feedback-form");
    let feedbackConfirm = document.getElementById("feedback-status-confirm");
    let feedbackFail = document.getElementById("feedback-status-fail");
    document.getElementById("feedback-btn").onclick = function () {
      feedbackModal.style.display = "block";
    };
    document.getElementById("cancelFeedbackBtn").onclick = function () {
      feedbackModal.style.display = "none";
    };

    document.getElementById("cancelFeedbackStatusBtn").onclick = function () {
      feedbackConfirm.style.display = "none";
      feedbackFail.style.display = "none";
    };
    
    document.getElementById("cancelFeedbackStatusBtn2").onclick = function () {
      feedbackConfirm.style.display = "none";
      feedbackFail.style.display = "none";
    };

    //override form function to instead emit save feedback event
    form.addEventListener("submit", function (e) {
        e.preventDefault();

        const data = new FormData(form);
        const feedback = data.get("feedback")?.trim();

        if (!feedback) return;
      
        socketio.emit("feedback_given", { feedback }, (response) => {
          if (response?.status === "True" || response?.status === true) {
            feedbackConfirm.style.display = "block";
            form.reset();
            document.getElementById("feedback-modal").style.display = "none";
          } else {
            feedbackFail.style.display = "block";
          }
        });
    });
      
    // add auto scroll
    function isNearBottom(container, threshold = 120) {
      const distanceFromBottom = container.scrollHeight - (container.scrollTop + container.clientHeight);
      return distanceFromBottom < threshold;
    }
    function smoothScrollToBottom(container) {
      container.scrollTo({ top: container.scrollHeight, behavior: "smooth" });
    }



  </script>
  <script type="text/javascript">
    const initialMessages = {{ messages | tojson }};
    initialMessages.forEach(msg => {
      createChatItem(msg.message, msg.sender);
    });
  </script>
</div>
{% endblock %}