diff --git a/direct.go b/direct.go
index b833fe9..37d1809 100644
--- a/direct.go
+++ b/direct.go
@@ -14,10 +14,11 @@ import (
)
var (
- WSClients = map[int64][]WSClient{}
- WSClientsMutex = sync.RWMutex{}
- WSAdmin = []WSClient{}
- WSAdminMutex = sync.RWMutex{}
+ OffsetQuestionTimer uint = 700
+ WSClients = map[int64][]WSClient{}
+ WSClientsMutex = sync.RWMutex{}
+ WSAdmin = []WSClient{}
+ WSAdminMutex = sync.RWMutex{}
)
func init() {
@@ -152,6 +153,7 @@ type WSMessage struct {
Stats map[string]interface{} `json:"stats,omitempty"`
UserId *int64 `json:"user,omitempty"`
Response string `json:"value,omitempty"`
+ Timer uint `json:"timer,omitempty"`
}
func (s *Survey) WSWriteAll(message WSMessage) {
@@ -271,7 +273,20 @@ func SurveyWSAdmin(w http.ResponseWriter, r *http.Request, ps httprouter.Params,
if survey, err := getSurvey(sid); err != nil {
log.Println("Unable to retrieve survey:", err)
} else {
- survey.Direct = v.QuestionId
+ if v.Timer > 0 {
+ if *survey.Direct != 0 {
+ var z int64 = 0
+ survey.Direct = &z
+ survey.Update()
+ }
+ go func() {
+ time.Sleep(time.Duration(OffsetQuestionTimer+v.Timer) * time.Millisecond)
+ survey.WSWriteAll(WSMessage{Action: "pause"})
+ WSAdminWriteAll(WSMessage{Action: "pause", SurveyId: &survey.Id})
+ }()
+ } else {
+ survey.Direct = v.QuestionId
+ }
_, err = survey.Update()
if err != nil {
log.Println("Unable to update survey:", err)
@@ -348,6 +363,20 @@ func SurveyWSAdmin(w http.ResponseWriter, r *http.Request, ps httprouter.Params,
log.Println("Unable to update:", err)
}
}
+ } else if v.Action == "mark_answered" && v.Response == "all" {
+ if survey, err := getSurvey(sid); err != nil {
+ log.Println("Unable to retrieve survey:", err)
+ } else if asks, err := survey.GetAsks(v.Response == ""); err != nil {
+ log.Println("Unable to retrieve asks:", err)
+ } else {
+ for _, ask := range asks {
+ ask.Answered = true
+ err = ask.Update()
+ if err != nil {
+ log.Println("Unable to update:", err)
+ }
+ }
+ }
} else {
log.Println("Unknown admin action:", v.Action)
}
@@ -370,7 +399,6 @@ func (s *Survey) WSAdminWriteAll(message WSMessage) {
defer WSAdminMutex.RUnlock()
for _, ws := range WSAdmin {
- log.Println("snd", message, ws.sid, s.Id)
if ws.sid == s.Id {
ws.c <- message
}
diff --git a/main.go b/main.go
index 7f4efeb..ec1cae8 100644
--- a/main.go
+++ b/main.go
@@ -63,6 +63,7 @@ func main() {
flag.StringVar(&DevProxy, "dev", DevProxy, "Proxify traffic to this host for static assets")
flag.StringVar(&baseURL, "baseurl", baseURL, "URL prepended to each URL")
flag.UintVar(¤tPromo, "current-promo", currentPromo, "Year of the current promotion")
+ flag.UintVar(&OffsetQuestionTimer, "offset-question-timer", OffsetQuestionTimer, "Duration to wait before sending pause msg in direct mode (in milliseconds)")
flag.Var(&localAuthUsers, "local-auth-user", "Allow local authentication for this user (bypass OIDC).")
flag.Parse()
diff --git a/ui/src/routes/surveys/[sid]/admin.svelte b/ui/src/routes/surveys/[sid]/admin.svelte
index 2ec87b1..b58aeef 100644
--- a/ui/src/routes/surveys/[sid]/admin.svelte
+++ b/ui/src/routes/surveys/[sid]/admin.svelte
@@ -13,6 +13,7 @@
import { user } from '../../../stores/user';
import SurveyAdmin from '../../../components/SurveyAdmin.svelte';
import SurveyBadge from '../../../components/SurveyBadge.svelte';
+ import { getSurvey } from '../../../lib/surveys';
import { getQuestions } from '../../../lib/questions';
import { getUsers } from '../../../lib/users';
@@ -29,6 +30,10 @@
}
});
+ function updateSurvey() {
+ surveyP = getSurvey(survey.id);
+ }
+
function updateQuestions() {
req_questions = getQuestions(survey.id);
}
@@ -38,6 +43,21 @@
let wsstats = null;
let current_question = null;
let responses = {};
+ let timer = 20000;
+ let timer_end = null;
+ let timer_remain = 0;
+ let timer_cancel = null;
+
+ function updTimer() {
+ const now = new Date().getTime();
+ if (now > timer_end) {
+ timer_remain = 0;
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ } else {
+ timer_remain = Math.floor((timer_end - now) / 100)/10;
+ }
+ }
let users = {};
function updateUsers() {
@@ -80,6 +100,7 @@
ws_up = false;
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason);
ws = null;
+ updateSurvey();
setTimeout(function() {
wsconnect();
}, 1500);
@@ -96,6 +117,16 @@
console.log(data);
if (data.action && data.action == "new_question") {
current_question = data.question;
+ if (timer_cancel) {
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ }
+ if (data.timer) {
+ timer_end = new Date().getTime() + data.timer;
+ timer_cancel = setInterval(updTimer, 250);
+ } else {
+ timer_end = null;
+ }
} else if (data.action && data.action == "stats") {
wsstats = data.stats;
} else if (data.action && data.action == "new_response") {
@@ -106,6 +137,11 @@
asks = asks;
} else {
current_question = null;
+ timer_end = null;
+ if (timer_cancel) {
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ }
}
});
}
@@ -118,9 +154,10 @@
{/if}
@@ -132,6 +169,11 @@
Administration
+ {#if asks.length}
+
+
+
+ {/if}
{#if survey.direct !== null}
{:else}
{#await req_questions}
@@ -166,6 +209,27 @@
Question
+ {#if timer_end}
+
+
+ ms
+
+ {:else}
+
+
+ ms
+
+ {/if}
+
|
{/each}
@@ -227,210 +299,218 @@
{/await}
- {/if}
-
-
-
-
- Questions
+
+
+
+
+
+ Questions
+ {#if asks.length}
+
+ {asks.length} question{#if asks.length > 1}s{/if}
+
+ {/if}
+
{#if asks.length}
-
- {asks.length} question{#if asks.length > 1}s{/if}
-
- {/if}
-
- {#if asks.length}
- {#each asks as ask (ask.id)}
-
- {/each}
- {:else}
-
- Pas de question pour l'instant.
-
- {/if}
-
-
-
-
- Réponses
-
- {#if Object.keys(responses).length}
- {#each Object.keys(responses) as q, qid (qid)}
- {#await req_questions then questions}
- {#each questions as question}
- {#if question.id == q}
-
- {question.title}
-
- {#if question.kind == 'ucq'}
- {#await question.getProposals()}
-
-
-
Chargement des propositions …
-
- {:then proposals}
-
-
-
- {#each proposals as proposal (proposal.id)}
-
-
- {proposal.label}
- |
-
- {responsesbyid[q].filter((e) => e == proposal.id.toString()).length}/{responsesbyid[q].length}
- |
-
- {Math.trunc(responsesbyid[q].filter((e) => e == proposal.id.toString()).length / responsesbyid[q].length * 1000)/10} %
- |
-
- {/each}
-
-
-
- {/await}
- {:else if question.kind == 'mcq'}
- {#await question.getProposals()}
-
-
-
Chargement des propositions …
-
- {:then proposals}
-
-
-
- {#each proposals as proposal (proposal.id)}
-
-
- {proposal.label}
- |
-
- {responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length}/{responsesbyid[q].length}
- |
-
- {Math.trunc(responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length / responsesbyid[q].length * 1000)/10} %
- |
-
- {/each}
-
-
-
- {/await}
- {:else}
-
- {/if}
- {/if}
- {/each}
- {/await}
- {/each}
- {/if}
-
-
-
-
-
- Connectés
- {#if wsstats}
- {wsstats.nb_clients} utilisateurs
- {/if}
-
- {#if wsstats}
-
- {#each wsstats.users as login, lid (lid)}
-
-
-
-
+ {#each asks as ask (ask.id)}
+
{/each}
-
+ {:else}
+
+ Pas de question pour l'instant.
+
+ {/if}
+
+
+
+
+ Réponses
+
+ {#if Object.keys(responses).length}
+ {#each Object.keys(responses) as q, qid (qid)}
+ {#await req_questions then questions}
+ {#each questions as question}
+ {#if question.id == q}
+
+ {question.title}
+
+ {#if question.kind == 'ucq'}
+ {#await question.getProposals()}
+
+
+
Chargement des propositions …
+
+ {:then proposals}
+
+
+
+ {#each proposals as proposal (proposal.id)}
+
+
+ {proposal.label}
+ |
+
+ {responsesbyid[q].filter((e) => e == proposal.id.toString()).length}/{responsesbyid[q].length}
+ |
+
+ {Math.trunc(responsesbyid[q].filter((e) => e == proposal.id.toString()).length / responsesbyid[q].length * 1000)/10} %
+ |
+
+ {/each}
+
+
+
+ {/await}
+ {:else if question.kind == 'mcq'}
+ {#await question.getProposals()}
+
+
+
Chargement des propositions …
+
+ {:then proposals}
+
+
+
+ {#each proposals as proposal (proposal.id)}
+
+
+ {proposal.label}
+ |
+
+ {responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length}/{responsesbyid[q].length}
+ |
+
+ {Math.trunc(responsesbyid[q].filter((e) => e.indexOf(proposal.id.toString()) >= 0).length / responsesbyid[q].length * 1000)/10} %
+ |
+
+ {/each}
+
+
+
+ {/await}
+ {:else}
+
+ {/if}
+ {/if}
+ {/each}
+ {/await}
+ {/each}
+ {/if}
+
+
+
+
+
+ Connectés
+ {#if wsstats}
+ {wsstats.nb_clients} utilisateurs
+ {/if}
+
+ {#if wsstats}
+
+ {#each wsstats.users as login, lid (lid)}
+
+
+
+
+
+
+ {/each}
+
+ {/if}
{/if}
{/await}
diff --git a/ui/src/routes/surveys/[sid]/live.svelte b/ui/src/routes/surveys/[sid]/live.svelte
index 6217a61..a4318da 100644
--- a/ui/src/routes/surveys/[sid]/live.svelte
+++ b/ui/src/routes/surveys/[sid]/live.svelte
@@ -28,6 +28,10 @@
let req_question;
let nosend = false;
+ let timer_init = null;
+ let timer_end = null;
+ let timer = 0;
+ let timer_cancel = null;
function afterQUpdate(q) {
value = undefined;
@@ -46,6 +50,19 @@
}
}
+ function updTimer() {
+ const now = new Date().getTime();
+ if (now > timer_end) {
+ timer = 100;
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ } else {
+ const dist1 = timer_end - timer_init;
+ const dist2 = timer_end - now;
+ timer = Math.ceil(100-dist2*100/dist1);
+ }
+ }
+
function wsconnect() {
const ws = new WebSocket((window.location.protocol == 'https'?'wss://':'ws://') + window.location.host + `/api/surveys/${sid}/ws`);
@@ -72,8 +89,25 @@
console.log(data);
if (data.action && data.action == "new_question") {
show_question = data.question;
+ if (timer_cancel) {
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ }
+ if (data.timer) {
+ timer_init = new Date().getTime();;
+ timer_end = timer_init + data.timer;
+ updTimer();
+ timer_cancel = setInterval(updTimer, 150);
+ } else {
+ timer_init = null;
+ }
} else {
show_question = null;
+ if (timer_cancel) {
+ clearInterval(timer_cancel);
+ timer_cancel = null;
+ }
+ timer_init = null;
}
});
}
@@ -153,12 +187,15 @@
= 100}
bind:value={value}
on:change={sendValue}
>
-
+ {#if timer_init}
+
+
85 && timer < 100} class:bg-danger={timer >= 100} role="progressbar" style="width: {timer}%">
+
+ {/if}
{#if question.kind != 'mcq' && question.kind != 'ucq'}