@olinfo/quizms-firebase 4.0.0-canary.20251207 → 4.0.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.
@@ -1,4 +1,4 @@
1
- const e = "4.0.0-canary.20251207", n = { "firebase-functions": "^7.0.0" }, s = {
1
+ const e = "4.0.0", n = { "firebase-functions": "^7.0.0" }, s = {
2
2
  version: e,
3
3
  dependencies: n
4
4
  };
@@ -1,41 +1,44 @@
1
- import { existsSync as d } from "node:fs";
2
- import { confirm as h } from "@inquirer/prompts";
3
- import { fatal as m, success as u } from "@olinfo/quizms/utils-node";
4
- import { SingleBar as w } from "cli-progress";
5
- import { partition as x } from "lodash-es";
6
- import b from "picocolors";
7
- async function E(f, a, s, r) {
8
- const t = new w({
1
+ import { existsSync as h } from "node:fs";
2
+ import { confirm as u } from "@inquirer/prompts";
3
+ import { AsyncPool as w } from "@olinfo/quizms/utils";
4
+ import { fatal as m, success as x } from "@olinfo/quizms/utils-node";
5
+ import { SingleBar as b } from "cli-progress";
6
+ import { partition as y } from "lodash-es";
7
+ import $ from "picocolors";
8
+ async function I(f, i, s, r) {
9
+ const t = new b({
9
10
  format: " {bar} {percentage}% | {value}/{total}",
10
11
  barCompleteChar: "█",
11
12
  barIncompleteChar: "▂"
12
13
  }), l = /* @__PURE__ */ new Set();
13
14
  t.start(s.length, 0), await Promise.all(
14
- s.map(async ([i, e]) => {
15
- d(i) || m(
16
- `Cannot find ${a}. Make sure to generate them first, use \`quizms --help\` for usage.`
15
+ s.map(async ([o, e]) => {
16
+ h(o) || m(
17
+ `Cannot find ${i}. Make sure to generate them first, use \`quizms --help\` for usage.`
17
18
  );
18
- const c = f.file(e), [g] = await c.exists().catch(() => [!1]);
19
- g && l.add(e), t.increment();
19
+ const g = f.file(e), [d] = await g.exists().catch(() => [!1]);
20
+ d && l.add(e), t.increment();
20
21
  })
21
22
  ), t.stop();
22
- const [o, p] = x(s, ([i, e]) => l.has(e));
23
- o.length > 0 && !r.skipExisting && !r.force && m(
24
- `Files already exist in \`${a}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
23
+ const [a, c] = y(s, ([o, e]) => l.has(e));
24
+ a.length > 0 && !r.skipExisting && !r.force && m(
25
+ `Files already exist in \`${i}\`. Use \`--force\` to overwrite or \`--skip-existing\` to ignore.`
25
26
  );
26
- const n = r.skipExisting ? p : [...o, ...p];
27
- r.force && o.length > 0 && (await h({
28
- message: `You are about to import the ${b.bold(
29
- a
30
- )}. ${o.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
27
+ const n = r.skipExisting ? c : [...a, ...c];
28
+ r.force && a.length > 0 && (await u({
29
+ message: `You are about to import the ${$.bold(
30
+ i
31
+ )}. ${a.length} files will be overwritten, the previous data will be lost. Are you really sure?`,
31
32
  default: !1
32
- }) || m("Command aborted.")), t.start(n.length, 0), await Promise.all(
33
- n.map(async ([i, e]) => {
34
- await f.upload(i, { destination: e }), t.increment();
35
- })
36
- ), t.stop(), u(`${n.length} ${a} imported!`);
33
+ }) || m("Command aborted.")), t.start(n.length, 0);
34
+ const p = new w(8);
35
+ for (const [o, e] of n)
36
+ p.run(async () => {
37
+ await f.upload(o, { destination: e }), t.increment();
38
+ });
39
+ await p.wait(), t.stop(), x(`${n.length} ${i} imported!`);
37
40
  }
38
41
  export {
39
- E as importStorage
42
+ I as importStorage
40
43
  };
41
44
  //# sourceMappingURL=storage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"storage.js","sources":["../../../../src/cli/utils/storage.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { fatal, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport { partition } from \"lodash-es\";\nimport pc from \"picocolors\";\n\ntype ImportOptions = {\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importStorage(\n bucket: Bucket,\n collection: string,\n files: [string, string][],\n options: ImportOptions,\n) {\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n\n const existingFiles = new Set<string>();\n\n bar.start(files.length, 0);\n await Promise.all(\n files.map(async ([local, remote]) => {\n if (!existsSync(local)) {\n fatal(\n `Cannot find ${collection}. Make sure to generate them first, use \\`quizms --help\\` for usage.`,\n );\n }\n\n const file = bucket.file(remote);\n const [exists] = await file.exists().catch(() => [false]);\n if (exists) {\n existingFiles.add(remote);\n }\n bar.increment();\n }),\n );\n bar.stop();\n\n const [existing, nonExisting] = partition(files, ([_, remote]) => existingFiles.has(remote));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Files already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const filesToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${pc.bold(\n collection,\n )}. ${existing.length} files will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n bar.start(filesToImport.length, 0);\n await Promise.all(\n filesToImport.map(async ([local, remote]) => {\n await bucket.upload(local, { destination: remote });\n bar.increment();\n }),\n );\n bar.stop();\n\n success(`${filesToImport.length} ${collection} imported!`);\n}\n"],"names":["importStorage","bucket","collection","files","options","bar","SingleBar","existingFiles","local","remote","existsSync","fatal","file","exists","existing","nonExisting","partition","_","filesToImport","confirm","pc","success"],"mappings":";;;;;;AAcA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB,GAEKC,wBAAoB,IAAA;AAE1B,EAAAF,EAAI,MAAMF,EAAM,QAAQ,CAAC,GACzB,MAAM,QAAQ;AAAA,IACZA,EAAM,IAAI,OAAO,CAACK,GAAOC,CAAM,MAAM;AACnC,MAAKC,EAAWF,CAAK,KACnBG;AAAA,QACE,eAAeT,CAAU;AAAA,MAAA;AAI7B,YAAMU,IAAOX,EAAO,KAAKQ,CAAM,GACzB,CAACI,CAAM,IAAI,MAAMD,EAAK,OAAA,EAAS,MAAM,MAAM,CAAC,EAAK,CAAC;AACxD,MAAIC,KACFN,EAAc,IAAIE,CAAM,GAE1BJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA;AAEJ,QAAM,CAACS,GAAUC,CAAW,IAAIC,EAAUb,GAAO,CAAC,CAACc,GAAGR,CAAM,MAAMF,EAAc,IAAIE,CAAM,CAAC;AAC3F,EAAIK,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DO;AAAA,IACE,4BAA4BT,CAAU;AAAA,EAAA;AAI1C,QAAMgB,IAAgBd,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEvF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMK,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC,EAAG;AAAA,MACzClB;AAAA,IAAA,CACD,KAAKY,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECH,EAAM,kBAAkB,IAI5BN,EAAI,MAAMa,EAAc,QAAQ,CAAC,GACjC,MAAM,QAAQ;AAAA,IACZA,EAAc,IAAI,OAAO,CAACV,GAAOC,CAAM,MAAM;AAC3C,YAAMR,EAAO,OAAOO,GAAO,EAAE,aAAaC,GAAQ,GAClDJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA,GAEJgB,EAAQ,GAAGH,EAAc,MAAM,IAAIhB,CAAU,YAAY;AAC3D;"}
1
+ {"version":3,"file":"storage.js","sources":["../../../../src/cli/utils/storage.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\n\nimport type { Bucket } from \"@google-cloud/storage\";\nimport { confirm } from \"@inquirer/prompts\";\nimport { AsyncPool } from \"@olinfo/quizms/utils\";\nimport { fatal, success } from \"@olinfo/quizms/utils-node\";\nimport { SingleBar } from \"cli-progress\";\nimport { partition } from \"lodash-es\";\nimport pc from \"picocolors\";\n\ntype ImportOptions = {\n skipExisting?: true;\n force?: true;\n};\n\nexport async function importStorage(\n bucket: Bucket,\n collection: string,\n files: [string, string][],\n options: ImportOptions,\n) {\n const bar = new SingleBar({\n format: \" {bar} {percentage}% | {value}/{total}\",\n barCompleteChar: \"\\u2588\",\n barIncompleteChar: \"\\u2582\",\n });\n\n const existingFiles = new Set<string>();\n\n bar.start(files.length, 0);\n await Promise.all(\n files.map(async ([local, remote]) => {\n if (!existsSync(local)) {\n fatal(\n `Cannot find ${collection}. Make sure to generate them first, use \\`quizms --help\\` for usage.`,\n );\n }\n\n const file = bucket.file(remote);\n const [exists] = await file.exists().catch(() => [false]);\n if (exists) {\n existingFiles.add(remote);\n }\n bar.increment();\n }),\n );\n bar.stop();\n\n const [existing, nonExisting] = partition(files, ([_, remote]) => existingFiles.has(remote));\n if (existing.length > 0 && !options.skipExisting && !options.force) {\n fatal(\n `Files already exist in \\`${collection}\\`. Use \\`--force\\` to overwrite or \\`--skip-existing\\` to ignore.`,\n );\n }\n\n const filesToImport = options.skipExisting ? nonExisting : [...existing, ...nonExisting];\n\n if (options.force && existing.length > 0) {\n const confirmed = await confirm({\n message: `You are about to import the ${pc.bold(\n collection,\n )}. ${existing.length} files will be overwritten, the previous data will be lost. Are you really sure?`,\n default: false,\n });\n if (!confirmed) {\n fatal(\"Command aborted.\");\n }\n }\n\n bar.start(filesToImport.length, 0);\n\n const pool = new AsyncPool(8);\n for (const [local, remote] of filesToImport) {\n void pool.run(async () => {\n await bucket.upload(local, { destination: remote });\n bar.increment();\n });\n }\n await pool.wait();\n\n bar.stop();\n\n success(`${filesToImport.length} ${collection} imported!`);\n}\n"],"names":["importStorage","bucket","collection","files","options","bar","SingleBar","existingFiles","local","remote","existsSync","fatal","file","exists","existing","nonExisting","partition","_","filesToImport","confirm","pc","pool","AsyncPool","success"],"mappings":";;;;;;;AAeA,eAAsBA,EACpBC,GACAC,GACAC,GACAC,GACA;AACA,QAAMC,IAAM,IAAIC,EAAU;AAAA,IACxB,QAAQ;AAAA,IACR,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,EAAA,CACpB,GAEKC,wBAAoB,IAAA;AAE1B,EAAAF,EAAI,MAAMF,EAAM,QAAQ,CAAC,GACzB,MAAM,QAAQ;AAAA,IACZA,EAAM,IAAI,OAAO,CAACK,GAAOC,CAAM,MAAM;AACnC,MAAKC,EAAWF,CAAK,KACnBG;AAAA,QACE,eAAeT,CAAU;AAAA,MAAA;AAI7B,YAAMU,IAAOX,EAAO,KAAKQ,CAAM,GACzB,CAACI,CAAM,IAAI,MAAMD,EAAK,OAAA,EAAS,MAAM,MAAM,CAAC,EAAK,CAAC;AACxD,MAAIC,KACFN,EAAc,IAAIE,CAAM,GAE1BJ,EAAI,UAAA;AAAA,IACN,CAAC;AAAA,EAAA,GAEHA,EAAI,KAAA;AAEJ,QAAM,CAACS,GAAUC,CAAW,IAAIC,EAAUb,GAAO,CAAC,CAACc,GAAGR,CAAM,MAAMF,EAAc,IAAIE,CAAM,CAAC;AAC3F,EAAIK,EAAS,SAAS,KAAK,CAACV,EAAQ,gBAAgB,CAACA,EAAQ,SAC3DO;AAAA,IACE,4BAA4BT,CAAU;AAAA,EAAA;AAI1C,QAAMgB,IAAgBd,EAAQ,eAAeW,IAAc,CAAC,GAAGD,GAAU,GAAGC,CAAW;AAEvF,EAAIX,EAAQ,SAASU,EAAS,SAAS,MACnB,MAAMK,EAAQ;AAAA,IAC9B,SAAS,+BAA+BC,EAAG;AAAA,MACzClB;AAAA,IAAA,CACD,KAAKY,EAAS,MAAM;AAAA,IACrB,SAAS;AAAA,EAAA,CACV,KAECH,EAAM,kBAAkB,IAI5BN,EAAI,MAAMa,EAAc,QAAQ,CAAC;AAEjC,QAAMG,IAAO,IAAIC,EAAU,CAAC;AAC5B,aAAW,CAACd,GAAOC,CAAM,KAAKS;AAC5B,IAAKG,EAAK,IAAI,YAAY;AACxB,YAAMpB,EAAO,OAAOO,GAAO,EAAE,aAAaC,GAAQ,GAClDJ,EAAI,UAAA;AAAA,IACN,CAAC;AAEH,QAAMgB,EAAK,KAAA,GAEXhB,EAAI,KAAA,GAEJkB,EAAQ,GAAGL,EAAc,MAAM,IAAIhB,CAAU,YAAY;AAC3D;"}
@@ -1,23 +1,23 @@
1
- import { jsx as t, jsxs as m, Fragment as b } from "react/jsx-runtime";
1
+ import { jsx as t, jsxs as u, Fragment as b } from "react/jsx-runtime";
2
2
  import { useMemo as w, useCallback as p, useEffect as v } from "react";
3
- import { Title as k, StudentFormField as x } from "@olinfo/quizms/components";
3
+ import { Title as x, StudentFormField as F } from "@olinfo/quizms/components";
4
4
  import { StudentProvider as I } from "@olinfo/quizms/student";
5
- import { Navbar as F, NavbarBrand as y, Form as A, SelectField as C, TextField as D, SubmitButton as W } from "@olinfo/react-components";
6
- import { signOut as j, getAuth as g, signInWithCustomToken as H } from "firebase/auth";
5
+ import { Navbar as k, NavbarBrand as A, Form as C, SelectField as D, TextField as W, SubmitButton as y } from "@olinfo/react-components";
6
+ import { signOut as E, getAuth as g, signInWithCustomToken as H } from "firebase/auth";
7
7
  import { waitForPendingWrites as f } from "firebase/firestore";
8
8
  import { isEqual as N } from "lodash-es";
9
- import { studentLogin as E } from "../common/api.js";
9
+ import { studentLogin as j } from "../common/api.js";
10
10
  import { useDb as S } from "../common/base-login.js";
11
- import { contestConverter as O, studentConverter as M, participationConverter as P } from "../common/converters.js";
12
- import { useWebsite as R } from "../common/website.js";
13
- import { useAuth as T } from "../hooks/auth.js";
11
+ import { contestConverter as M, studentConverter as P, participationConverter as R } from "../common/converters.js";
12
+ import { useWebsite as T } from "../common/website.js";
13
+ import { useAuth as q } from "../hooks/auth.js";
14
14
  import { useCollection as z } from "../hooks/collection.js";
15
15
  import { useDocument as h } from "../hooks/document.js";
16
16
  import { StudentRestoring as B } from "./student-restoring.js";
17
- import { FirebaseStatement as q } from "./student-statement.js";
17
+ import { FirebaseStatement as O } from "./student-statement.js";
18
18
  import G from "virtual:quizms-firebase-header";
19
- function mt() {
20
- const e = T("student"), o = R(), [a] = z("contests", O, {
19
+ function ut() {
20
+ const e = q("student"), o = T(), [a] = z("contests", M, {
21
21
  constraints: { id: o.contests },
22
22
  subscribe: !0
23
23
  });
@@ -32,13 +32,13 @@ function mt() {
32
32
  }
33
33
  function L({ contests: e }) {
34
34
  const o = S(), a = p(
35
- async ({ contestId: i, token: s, ...n }) => {
36
- const d = await E(o, {
37
- contestId: i,
35
+ async ({ contestId: n, token: s, ...i }) => {
36
+ const d = await j(o, {
37
+ contestId: n,
38
38
  token: s,
39
39
  userData: {
40
- ...n,
41
- birthDate: n.birthDate ? new Date(n.birthDate) : void 0
40
+ ...i,
41
+ birthDate: i.birthDate ? new Date(i.birthDate) : void 0
42
42
  },
43
43
  extraData: {
44
44
  userAgent: navigator.userAgent,
@@ -49,36 +49,36 @@ function L({ contests: e }) {
49
49
  pixelRatio: window.devicePixelRatio,
50
50
  darkMode: window.matchMedia("(prefers-color-scheme: dark)").matches
51
51
  }
52
- }), u = g(o.app);
53
- await H(u, d.token);
52
+ }), m = g(o.app);
53
+ await H(m, d.token), await document.documentElement.requestFullscreen();
54
54
  },
55
55
  [o]
56
56
  ), r = w(
57
57
  () => ({
58
- contestId: Object.keys(e).length === 1 ? Object.keys(e)[0] : void 0
58
+ contestId: e.length === 1 ? e[0].id : void 0
59
59
  }),
60
60
  [e]
61
61
  );
62
- return /* @__PURE__ */ m(b, { children: [
63
- /* @__PURE__ */ t(F, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ t(y, { children: /* @__PURE__ */ t("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ t(k, {}) }) }) }),
64
- /* @__PURE__ */ m(A, { defaultValue: r, onSubmit: a, className: "p-4 pb-8", children: [
62
+ return /* @__PURE__ */ u(b, { children: [
63
+ /* @__PURE__ */ t(k, { color: "bg-base-300 text-base-content", children: /* @__PURE__ */ t(A, { children: /* @__PURE__ */ t("div", { className: "flex items-center h-full font-bold", children: /* @__PURE__ */ t(x, {}) }) }) }),
64
+ /* @__PURE__ */ u(C, { defaultValue: r, onSubmit: a, className: "p-4 pb-8", children: [
65
65
  /* @__PURE__ */ t("h1", { className: "mb-2 text-xl font-bold", children: "Accedi alla gara" }),
66
66
  /* @__PURE__ */ t(
67
- C,
67
+ D,
68
68
  {
69
69
  field: "contestId",
70
70
  label: "Gara",
71
- options: Object.fromEntries(e.map((i) => [i.id, i.name])),
71
+ options: Object.fromEntries(e.map((n) => [n.id, n.name])),
72
72
  placeholder: "Seleziona una gara"
73
73
  }
74
74
  ),
75
- ({ contestId: i }) => {
76
- const s = e.find((n) => n.id === i);
75
+ ({ contestId: n }) => {
76
+ const s = e.find((i) => i.id === n);
77
77
  if (s)
78
- return /* @__PURE__ */ m(b, { children: [
79
- s.userData.map((n) => /* @__PURE__ */ t(x, { field: n }, n.name)),
80
- /* @__PURE__ */ t(D, { field: "token", label: "Codice prova", placeholder: "aaaaa-bbbbb-ccccc" }),
81
- /* @__PURE__ */ t(W, { children: "Inizia la prova" })
78
+ return /* @__PURE__ */ u(b, { children: [
79
+ s.userData.map((i) => /* @__PURE__ */ t(F, { field: i }, i.name)),
80
+ /* @__PURE__ */ t(W, { field: "token", label: "Codice prova", placeholder: "aaaaa-bbbbb-ccccc" }),
81
+ /* @__PURE__ */ t(y, { children: "Inizia la prova" })
82
82
  ] });
83
83
  }
84
84
  ] })
@@ -89,42 +89,43 @@ function V({
89
89
  participationId: o,
90
90
  studentId: a
91
91
  }) {
92
- const r = S(), [i, s] = h(
92
+ const r = S(), [n, s] = h(
93
93
  `participations/${o}/students`,
94
94
  a,
95
- M,
95
+ P,
96
96
  { subscribe: !0 }
97
- ), [n] = h("participations", o, P, {
97
+ ), [i] = h("participations", o, R, {
98
98
  subscribe: !0
99
99
  }), d = w(
100
- () => e.find((c) => c.id === i.contestId),
101
- [e, i.contestId]
102
- ), u = p(async () => {
100
+ () => e.find((c) => c.id === n.contestId),
101
+ [e, n.contestId]
102
+ ), m = p(async () => {
103
103
  await f(r);
104
104
  }, [r]), l = p(async () => {
105
- await f(r), await j(g(r.app)), window.location.reload();
105
+ await f(r), await E(g(r.app)), window.location.reload();
106
106
  }, [r]);
107
107
  return v(() => {
108
- n.token !== i.token && l();
109
- }, [n.token, i.token, l]), /* @__PURE__ */ m(
108
+ i.token !== n.token && l();
109
+ }, [i.token, n.token, l]), /* @__PURE__ */ u(
110
110
  I,
111
111
  {
112
112
  contest: d,
113
- participation: n,
114
- student: i,
113
+ participation: i,
114
+ student: n,
115
115
  setStudent: async (c) => {
116
- N(i, c) || await s({ ...c });
116
+ N(n, c) || await s({ ...c });
117
117
  },
118
118
  logout: l,
119
- onSubmit: u,
119
+ onSubmit: m,
120
+ enforceFullscreen: !0,
120
121
  children: [
121
122
  /* @__PURE__ */ t(G, { contestId: d.id }),
122
- /* @__PURE__ */ t(q, {})
123
+ /* @__PURE__ */ t(O, {})
123
124
  ]
124
125
  }
125
126
  );
126
127
  }
127
128
  export {
128
- mt as default
129
+ ut as default
129
130
  };
130
131
  //# sourceMappingURL=student.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"student.js","sources":["../../../../src/web/entry/student.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo } from \"react\";\n\nimport { StudentFormField, Title } from \"@olinfo/quizms/components\";\nimport type { Contest, Student } from \"@olinfo/quizms/models\";\nimport { StudentProvider } from \"@olinfo/quizms/student\";\nimport {\n Form,\n Navbar,\n NavbarBrand,\n SelectField,\n SubmitButton,\n TextField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, signOut } from \"firebase/auth\";\nimport { waitForPendingWrites } from \"firebase/firestore\";\nimport { isEqual } from \"lodash-es\";\n\nimport { studentLogin } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n} from \"~/web/common/converters\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useAuth, useCollection, useDocument } from \"~/web/hooks\";\n\nimport { StudentRestoring } from \"./student-restoring\";\nimport { FirebaseStatement } from \"./student-statement\";\n// @ts-expect-error\nimport Header from \"virtual:quizms-firebase-header\";\n\nexport default function StudentEntry() {\n const auth = useAuth(\"student\");\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n subscribe: true,\n });\n\n if (auth) {\n return (\n <StudentRestoring user={auth.user}>\n <StudentInner\n contests={contests}\n participationId={auth.claims.participationId}\n studentId={auth.claims.studentId}\n />\n </StudentRestoring>\n );\n }\n\n return <StudentForm contests={contests} />;\n}\n\ntype FormStudent = {\n contestId: string;\n token: string;\n} & Student[\"userData\"];\n\nfunction StudentForm({ contests }: { contests: Contest[] }) {\n const db = useDb();\n\n const submit = useCallback(\n async ({ contestId, token, ...userData }: FormStudent) => {\n const data = await studentLogin(db, {\n contestId,\n token,\n userData: {\n ...userData,\n birthDate: userData.birthDate ? new Date(userData.birthDate) : undefined,\n },\n extraData: {\n userAgent: navigator.userAgent,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n screenWidth: window.screen.availWidth,\n screenHeight: window.screen.availHeight,\n pixelRatio: window.devicePixelRatio,\n darkMode: window.matchMedia(\"(prefers-color-scheme: dark)\").matches,\n },\n });\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, data.token);\n },\n [db],\n );\n\n const defaultValue = useMemo(\n () => ({\n contestId: Object.keys(contests).length === 1 ? Object.keys(contests)[0] : undefined,\n }),\n [contests],\n );\n\n return (\n <>\n <Navbar color=\"bg-base-300 text-base-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n </Navbar>\n <Form defaultValue={defaultValue} onSubmit={submit} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gara</h1>\n <SelectField\n field=\"contestId\"\n label=\"Gara\"\n options={Object.fromEntries(contests.map((c) => [c.id, c.name]))}\n placeholder=\"Seleziona una gara\"\n />\n {({ contestId }) => {\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) return;\n return (\n <>\n {contest.userData.map((field) => (\n <StudentFormField key={field.name} field={field} />\n ))}\n <TextField field=\"token\" label=\"Codice prova\" placeholder=\"aaaaa-bbbbb-ccccc\" />\n <SubmitButton>Inizia la prova</SubmitButton>\n </>\n );\n }}\n </Form>\n </>\n );\n}\n\nfunction StudentInner({\n contests,\n participationId,\n studentId,\n}: {\n contests: Contest[];\n participationId: string;\n studentId: string;\n}) {\n const db = useDb();\n\n const [student, setStudent] = useDocument(\n `participations/${participationId}/students`,\n studentId,\n studentConverter,\n { subscribe: true },\n );\n const [participation] = useDocument(\"participations\", participationId, participationConverter, {\n subscribe: true,\n });\n\n const contest = useMemo(\n () => contests.find((c) => c.id === student.contestId)!,\n [contests, student.contestId],\n );\n\n const onSubmit = useCallback(async () => {\n await waitForPendingWrites(db);\n }, [db]);\n\n const logout = useCallback(async () => {\n await waitForPendingWrites(db);\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n useEffect(() => {\n if (participation.token !== student.token) {\n void logout();\n }\n }, [participation.token, student.token, logout]);\n\n const setStudentAndSubmission = async (newStudent: Student) => {\n if (!isEqual(student, newStudent)) {\n await setStudent({ ...newStudent });\n }\n };\n\n return (\n <StudentProvider\n contest={contest}\n participation={participation}\n student={student}\n setStudent={setStudentAndSubmission}\n logout={logout}\n onSubmit={onSubmit}>\n <Header contestId={contest.id} />\n <FirebaseStatement />\n </StudentProvider>\n );\n}\n"],"names":["StudentEntry","auth","useAuth","website","useWebsite","contests","useCollection","contestConverter","jsx","StudentRestoring","StudentInner","StudentForm","db","useDb","submit","useCallback","contestId","token","userData","data","studentLogin","getAuth","signInWithCustomToken","defaultValue","useMemo","jsxs","Fragment","Navbar","NavbarBrand","Title","Form","SelectField","c","contest","field","StudentFormField","TextField","SubmitButton","participationId","studentId","student","setStudent","useDocument","studentConverter","participation","participationConverter","onSubmit","waitForPendingWrites","logout","signOut","useEffect","StudentProvider","newStudent","isEqual","Header","FirebaseStatement"],"mappings":";;;;;;;;;;;;;;;;;;AAgCA,SAAwBA,KAAe;AACrC,QAAMC,IAAOC,EAAQ,SAAS,GAExBC,IAAUC,EAAA,GACV,CAACC,CAAQ,IAAIC,EAAc,YAAYC,GAAkB;AAAA,IAC7D,aAAa,EAAE,IAAIJ,EAAQ,SAAA;AAAA,IAC3B,WAAW;AAAA,EAAA,CACZ;AAED,SAAIF,IAEA,gBAAAO,EAACC,GAAA,EAAiB,MAAMR,EAAK,MAC3B,UAAA,gBAAAO;AAAA,IAACE;AAAA,IAAA;AAAA,MACC,UAAAL;AAAA,MACA,iBAAiBJ,EAAK,OAAO;AAAA,MAC7B,WAAWA,EAAK,OAAO;AAAA,IAAA;AAAA,EAAA,GAE3B,IAIG,gBAAAO,EAACG,KAAY,UAAAN,GAAoB;AAC1C;AAOA,SAASM,EAAY,EAAE,UAAAN,KAAqC;AAC1D,QAAMO,IAAKC,EAAA,GAELC,IAASC;AAAA,IACb,OAAO,EAAE,WAAAC,GAAW,OAAAC,GAAO,GAAGC,QAA4B;AACxD,YAAMC,IAAO,MAAMC,EAAaR,GAAI;AAAA,QAClC,WAAAI;AAAA,QACA,OAAAC;AAAA,QACA,UAAU;AAAA,UACR,GAAGC;AAAA,UACH,WAAWA,EAAS,YAAY,IAAI,KAAKA,EAAS,SAAS,IAAI;AAAA,QAAA;AAAA,QAEjE,WAAW;AAAA,UACT,WAAW,UAAU;AAAA,UACrB,aAAa,OAAO;AAAA,UACpB,cAAc,OAAO;AAAA,UACrB,aAAa,OAAO,OAAO;AAAA,UAC3B,cAAc,OAAO,OAAO;AAAA,UAC5B,YAAY,OAAO;AAAA,UACnB,UAAU,OAAO,WAAW,8BAA8B,EAAE;AAAA,QAAA;AAAA,MAC9D,CACD,GAEKjB,IAAOoB,EAAQT,EAAG,GAAG;AAC3B,YAAMU,EAAsBrB,GAAMkB,EAAK,KAAK;AAAA,IAC9C;AAAA,IACA,CAACP,CAAE;AAAA,EAAA,GAGCW,IAAeC;AAAA,IACnB,OAAO;AAAA,MACL,WAAW,OAAO,KAAKnB,CAAQ,EAAE,WAAW,IAAI,OAAO,KAAKA,CAAQ,EAAE,CAAC,IAAI;AAAA,IAAA;AAAA,IAE7E,CAACA,CAAQ;AAAA,EAAA;AAGX,SACE,gBAAAoB,EAAAC,GAAA,EACE,UAAA;AAAA,IAAA,gBAAAlB,EAACmB,GAAA,EAAO,OAAM,iCACZ,UAAA,gBAAAnB,EAACoB,GAAA,EACC,UAAA,gBAAApB,EAAC,OAAA,EAAI,WAAU,sCACb,UAAA,gBAAAA,EAACqB,GAAA,EAAM,EAAA,CACT,GACF,GACF;AAAA,sBACCC,GAAA,EAAK,cAAAP,GAA4B,UAAUT,GAAQ,WAAU,YAC5D,UAAA;AAAA,MAAA,gBAAAN,EAAC,MAAA,EAAG,WAAU,0BAAyB,UAAA,oBAAgB;AAAA,MACvD,gBAAAA;AAAA,QAACuB;AAAA,QAAA;AAAA,UACC,OAAM;AAAA,UACN,OAAM;AAAA,UACN,SAAS,OAAO,YAAY1B,EAAS,IAAI,CAAC2B,MAAM,CAACA,EAAE,IAAIA,EAAE,IAAI,CAAC,CAAC;AAAA,UAC/D,aAAY;AAAA,QAAA;AAAA,MAAA;AAAA,MAEb,CAAC,EAAE,WAAAhB,EAAA,MAAgB;AAClB,cAAMiB,IAAU5B,EAAS,KAAK,CAAC2B,MAAMA,EAAE,OAAOhB,CAAS;AACvD,YAAKiB;AACL,iBACE,gBAAAR,EAAAC,GAAA,EACG,UAAA;AAAA,YAAAO,EAAQ,SAAS,IAAI,CAACC,wBACpBC,GAAA,EAAkC,OAAAD,EAAA,GAAZA,EAAM,IAAoB,CAClD;AAAA,8BACAE,GAAA,EAAU,OAAM,SAAQ,OAAM,gBAAe,aAAY,qBAAoB;AAAA,YAC9E,gBAAA5B,EAAC6B,KAAa,UAAA,kBAAA,CAAe;AAAA,UAAA,GAC/B;AAAA,MAEJ;AAAA,IAAA,EAAA,CACF;AAAA,EAAA,GACF;AAEJ;AAEA,SAAS3B,EAAa;AAAA,EACpB,UAAAL;AAAA,EACA,iBAAAiC;AAAA,EACA,WAAAC;AACF,GAIG;AACD,QAAM3B,IAAKC,EAAA,GAEL,CAAC2B,GAASC,CAAU,IAAIC;AAAA,IAC5B,kBAAkBJ,CAAe;AAAA,IACjCC;AAAA,IACAI;AAAA,IACA,EAAE,WAAW,GAAA;AAAA,EAAK,GAEd,CAACC,CAAa,IAAIF,EAAY,kBAAkBJ,GAAiBO,GAAwB;AAAA,IAC7F,WAAW;AAAA,EAAA,CACZ,GAEKZ,IAAUT;AAAA,IACd,MAAMnB,EAAS,KAAK,CAAC,MAAM,EAAE,OAAOmC,EAAQ,SAAS;AAAA,IACrD,CAACnC,GAAUmC,EAAQ,SAAS;AAAA,EAAA,GAGxBM,IAAW/B,EAAY,YAAY;AACvC,UAAMgC,EAAqBnC,CAAE;AAAA,EAC/B,GAAG,CAACA,CAAE,CAAC,GAEDoC,IAASjC,EAAY,YAAY;AACrC,UAAMgC,EAAqBnC,CAAE,GAC7B,MAAMqC,EAAQ5B,EAAQT,EAAG,GAAG,CAAC,GAC7B,OAAO,SAAS,OAAA;AAAA,EAClB,GAAG,CAACA,CAAE,CAAC;AAEP,SAAAsC,EAAU,MAAM;AACd,IAAIN,EAAc,UAAUJ,EAAQ,SAC7BQ,EAAA;AAAA,EAET,GAAG,CAACJ,EAAc,OAAOJ,EAAQ,OAAOQ,CAAM,CAAC,GAS7C,gBAAAvB;AAAA,IAAC0B;AAAA,IAAA;AAAA,MACC,SAAAlB;AAAA,MACA,eAAAW;AAAA,MACA,SAAAJ;AAAA,MACA,YAX4B,OAAOY,MAAwB;AAC7D,QAAKC,EAAQb,GAASY,CAAU,KAC9B,MAAMX,EAAW,EAAE,GAAGW,GAAY;AAAA,MAEtC;AAAA,MAQI,QAAAJ;AAAA,MACA,UAAAF;AAAA,MACA,UAAA;AAAA,QAAA,gBAAAtC,EAAC8C,GAAA,EAAO,WAAWrB,EAAQ,GAAA,CAAI;AAAA,0BAC9BsB,GAAA,CAAA,CAAkB;AAAA,MAAA;AAAA,IAAA;AAAA,EAAA;AAGzB;"}
1
+ {"version":3,"file":"student.js","sources":["../../../../src/web/entry/student.tsx"],"sourcesContent":["import { useCallback, useEffect, useMemo } from \"react\";\n\nimport { StudentFormField, Title } from \"@olinfo/quizms/components\";\nimport type { Contest, Student } from \"@olinfo/quizms/models\";\nimport { StudentProvider } from \"@olinfo/quizms/student\";\nimport {\n Form,\n Navbar,\n NavbarBrand,\n SelectField,\n SubmitButton,\n TextField,\n} from \"@olinfo/react-components\";\nimport { getAuth, signInWithCustomToken, signOut } from \"firebase/auth\";\nimport { waitForPendingWrites } from \"firebase/firestore\";\nimport { isEqual } from \"lodash-es\";\n\nimport { studentLogin } from \"~/web/common/api\";\nimport { useDb } from \"~/web/common/base-login\";\nimport {\n contestConverter,\n participationConverter,\n studentConverter,\n} from \"~/web/common/converters\";\nimport { useWebsite } from \"~/web/common/website\";\nimport { useAuth, useCollection, useDocument } from \"~/web/hooks\";\n\nimport { StudentRestoring } from \"./student-restoring\";\nimport { FirebaseStatement } from \"./student-statement\";\n// @ts-expect-error\nimport Header from \"virtual:quizms-firebase-header\";\n\nexport default function StudentEntry() {\n const auth = useAuth(\"student\");\n\n const website = useWebsite();\n const [contests] = useCollection(\"contests\", contestConverter, {\n constraints: { id: website.contests },\n subscribe: true,\n });\n\n if (auth) {\n return (\n <StudentRestoring user={auth.user}>\n <StudentInner\n contests={contests}\n participationId={auth.claims.participationId}\n studentId={auth.claims.studentId}\n />\n </StudentRestoring>\n );\n }\n\n return <StudentForm contests={contests} />;\n}\n\ntype FormStudent = {\n contestId: string;\n token: string;\n} & Student[\"userData\"];\n\nfunction StudentForm({ contests }: { contests: Contest[] }) {\n const db = useDb();\n\n const submit = useCallback(\n async ({ contestId, token, ...userData }: FormStudent) => {\n const data = await studentLogin(db, {\n contestId,\n token,\n userData: {\n ...userData,\n birthDate: userData.birthDate ? new Date(userData.birthDate) : undefined,\n },\n extraData: {\n userAgent: navigator.userAgent,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n screenWidth: window.screen.availWidth,\n screenHeight: window.screen.availHeight,\n pixelRatio: window.devicePixelRatio,\n darkMode: window.matchMedia(\"(prefers-color-scheme: dark)\").matches,\n },\n });\n\n const auth = getAuth(db.app);\n await signInWithCustomToken(auth, data.token);\n await document.documentElement.requestFullscreen();\n },\n [db],\n );\n\n const defaultValue = useMemo(\n () => ({\n contestId: contests.length === 1 ? contests[0].id : undefined,\n }),\n [contests],\n );\n\n return (\n <>\n <Navbar color=\"bg-base-300 text-base-content\">\n <NavbarBrand>\n <div className=\"flex items-center h-full font-bold\">\n <Title />\n </div>\n </NavbarBrand>\n </Navbar>\n <Form defaultValue={defaultValue} onSubmit={submit} className=\"p-4 pb-8\">\n <h1 className=\"mb-2 text-xl font-bold\">Accedi alla gara</h1>\n <SelectField\n field=\"contestId\"\n label=\"Gara\"\n options={Object.fromEntries(contests.map((c) => [c.id, c.name]))}\n placeholder=\"Seleziona una gara\"\n />\n {({ contestId }) => {\n const contest = contests.find((c) => c.id === contestId);\n if (!contest) return;\n return (\n <>\n {contest.userData.map((field) => (\n <StudentFormField key={field.name} field={field} />\n ))}\n <TextField field=\"token\" label=\"Codice prova\" placeholder=\"aaaaa-bbbbb-ccccc\" />\n <SubmitButton>Inizia la prova</SubmitButton>\n </>\n );\n }}\n </Form>\n </>\n );\n}\n\nfunction StudentInner({\n contests,\n participationId,\n studentId,\n}: {\n contests: Contest[];\n participationId: string;\n studentId: string;\n}) {\n const db = useDb();\n\n const [student, setStudent] = useDocument(\n `participations/${participationId}/students`,\n studentId,\n studentConverter,\n { subscribe: true },\n );\n const [participation] = useDocument(\"participations\", participationId, participationConverter, {\n subscribe: true,\n });\n\n const contest = useMemo(\n () => contests.find((c) => c.id === student.contestId)!,\n [contests, student.contestId],\n );\n\n const onSubmit = useCallback(async () => {\n await waitForPendingWrites(db);\n }, [db]);\n\n const logout = useCallback(async () => {\n await waitForPendingWrites(db);\n await signOut(getAuth(db.app));\n window.location.reload();\n }, [db]);\n\n useEffect(() => {\n if (participation.token !== student.token) {\n void logout();\n }\n }, [participation.token, student.token, logout]);\n\n const setStudentAndSubmission = async (newStudent: Student) => {\n if (!isEqual(student, newStudent)) {\n await setStudent({ ...newStudent });\n }\n };\n\n return (\n <StudentProvider\n contest={contest}\n participation={participation}\n student={student}\n setStudent={setStudentAndSubmission}\n logout={logout}\n onSubmit={onSubmit}\n enforceFullscreen={true}>\n <Header contestId={contest.id} />\n <FirebaseStatement />\n </StudentProvider>\n );\n}\n"],"names":["StudentEntry","auth","useAuth","website","useWebsite","contests","useCollection","contestConverter","jsx","StudentRestoring","StudentInner","StudentForm","db","useDb","submit","useCallback","contestId","token","userData","data","studentLogin","getAuth","signInWithCustomToken","defaultValue","useMemo","jsxs","Fragment","Navbar","NavbarBrand","Title","Form","SelectField","c","contest","field","StudentFormField","TextField","SubmitButton","participationId","studentId","student","setStudent","useDocument","studentConverter","participation","participationConverter","onSubmit","waitForPendingWrites","logout","signOut","useEffect","StudentProvider","newStudent","isEqual","Header","FirebaseStatement"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olinfo/quizms-firebase",
3
- "version": "4.0.0-canary.20251207",
3
+ "version": "4.0.0",
4
4
  "type": "module",
5
5
  "author": "Alessandro Bortolin <bortolin.alessandro@outlook.it>",
6
6
  "license": "MIT",
@@ -56,7 +56,7 @@
56
56
  "vite-tsconfig-paths": "^5.1.4",
57
57
  "wouter": "^3.8.1",
58
58
  "zod": "^3.25.76",
59
- "@olinfo/quizms": "^4.0.0-canary.20251207"
59
+ "@olinfo/quizms": "^4.0.0"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "react": "~19.1"