@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.
package/dist/package.json.js
CHANGED
|
@@ -1,41 +1,44 @@
|
|
|
1
|
-
import { existsSync as
|
|
2
|
-
import { confirm as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
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 ([
|
|
15
|
-
|
|
16
|
-
`Cannot find ${
|
|
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
|
|
19
|
-
|
|
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 [
|
|
23
|
-
|
|
24
|
-
`Files already exist in \`${
|
|
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 ?
|
|
27
|
-
r.force &&
|
|
28
|
-
message: `You are about to import the ${
|
|
29
|
-
|
|
30
|
-
)}. ${
|
|
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)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
6
|
-
import { signOut as
|
|
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
|
|
9
|
+
import { studentLogin as j } from "../common/api.js";
|
|
10
10
|
import { useDb as S } from "../common/base-login.js";
|
|
11
|
-
import { contestConverter as
|
|
12
|
-
import { useWebsite as
|
|
13
|
-
import { useAuth as
|
|
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
|
|
17
|
+
import { FirebaseStatement as O } from "./student-statement.js";
|
|
18
18
|
import G from "virtual:quizms-firebase-header";
|
|
19
|
-
function
|
|
20
|
-
const e =
|
|
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:
|
|
36
|
-
const d = await
|
|
37
|
-
contestId:
|
|
35
|
+
async ({ contestId: n, token: s, ...i }) => {
|
|
36
|
+
const d = await j(o, {
|
|
37
|
+
contestId: n,
|
|
38
38
|
token: s,
|
|
39
39
|
userData: {
|
|
40
|
-
...
|
|
41
|
-
birthDate:
|
|
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
|
-
}),
|
|
53
|
-
await H(
|
|
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:
|
|
58
|
+
contestId: e.length === 1 ? e[0].id : void 0
|
|
59
59
|
}),
|
|
60
60
|
[e]
|
|
61
61
|
);
|
|
62
|
-
return /* @__PURE__ */
|
|
63
|
-
/* @__PURE__ */ t(
|
|
64
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
67
|
+
D,
|
|
68
68
|
{
|
|
69
69
|
field: "contestId",
|
|
70
70
|
label: "Gara",
|
|
71
|
-
options: Object.fromEntries(e.map((
|
|
71
|
+
options: Object.fromEntries(e.map((n) => [n.id, n.name])),
|
|
72
72
|
placeholder: "Seleziona una gara"
|
|
73
73
|
}
|
|
74
74
|
),
|
|
75
|
-
({ contestId:
|
|
76
|
-
const s = e.find((
|
|
75
|
+
({ contestId: n }) => {
|
|
76
|
+
const s = e.find((i) => i.id === n);
|
|
77
77
|
if (s)
|
|
78
|
-
return /* @__PURE__ */
|
|
79
|
-
s.userData.map((
|
|
80
|
-
/* @__PURE__ */ t(
|
|
81
|
-
/* @__PURE__ */ t(
|
|
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(), [
|
|
92
|
+
const r = S(), [n, s] = h(
|
|
93
93
|
`participations/${o}/students`,
|
|
94
94
|
a,
|
|
95
|
-
|
|
95
|
+
P,
|
|
96
96
|
{ subscribe: !0 }
|
|
97
|
-
), [
|
|
97
|
+
), [i] = h("participations", o, R, {
|
|
98
98
|
subscribe: !0
|
|
99
99
|
}), d = w(
|
|
100
|
-
() => e.find((c) => c.id ===
|
|
101
|
-
[e,
|
|
102
|
-
),
|
|
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
|
|
105
|
+
await f(r), await E(g(r.app)), window.location.reload();
|
|
106
106
|
}, [r]);
|
|
107
107
|
return v(() => {
|
|
108
|
-
|
|
109
|
-
}, [
|
|
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:
|
|
114
|
-
student:
|
|
113
|
+
participation: i,
|
|
114
|
+
student: n,
|
|
115
115
|
setStudent: async (c) => {
|
|
116
|
-
N(
|
|
116
|
+
N(n, c) || await s({ ...c });
|
|
117
117
|
},
|
|
118
118
|
logout: l,
|
|
119
|
-
onSubmit:
|
|
119
|
+
onSubmit: m,
|
|
120
|
+
enforceFullscreen: !0,
|
|
120
121
|
children: [
|
|
121
122
|
/* @__PURE__ */ t(G, { contestId: d.id }),
|
|
122
|
-
/* @__PURE__ */ t(
|
|
123
|
+
/* @__PURE__ */ t(O, {})
|
|
123
124
|
]
|
|
124
125
|
}
|
|
125
126
|
);
|
|
126
127
|
}
|
|
127
128
|
export {
|
|
128
|
-
|
|
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:
|
|
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
|
|
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
|
|
59
|
+
"@olinfo/quizms": "^4.0.0"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
62
|
"react": "~19.1"
|