diff --git a/packages/backend/src/actors/FingerprintStore.ts b/packages/backend/src/actors/FingerprintStore.ts index 9dc4f4b8..3a43a07a 100644 --- a/packages/backend/src/actors/FingerprintStore.ts +++ b/packages/backend/src/actors/FingerprintStore.ts @@ -50,14 +50,11 @@ export class FingerprintStore extends SubscribableActor>(message, { StoreFingerprint: async fingerprint => { - this.state = await create(this.state, async draft => { - const currentFingerprint = (await this.getEntity(fingerprint.uid)).orElse({} as Fingerprint); - fingerprint.updated = toTimestamp(); - const newFingerprint = { ...currentFingerprint, ...fingerprint }; - draft.cache.set(fingerprint.uid, newFingerprint); - logger.debug(`Storing new fingerprint ${JSON.stringify(newFingerprint)}`); - this.storeEntity(newFingerprint); - }); + const currentFingerprint = (await this.getEntity(fingerprint.uid)).orElse({} as Fingerprint); + fingerprint.updated = toTimestamp(); + const newFingerprint = { ...currentFingerprint, ...fingerprint }; + logger.debug(`Storing new fingerprint ${JSON.stringify(newFingerprint)}`); + await this.storeEntity(newFingerprint); return unit(); }, Get: async id => { @@ -66,49 +63,42 @@ export class FingerprintStore extends SubscribableActor { - const mbFingerprint = await this.getEntity(id) - const {uid} = await this.ask("actors://recapp-backend/UserStore", UserStoreMessages.GetByFingerprint(id)); - return mbFingerprint.match( - fp => { - this.storeEntity({...fp, blocked: true}); - this.updateSubscribers({...fp, blocked: true}) - if (uid) - this.send("actors://recapp-backend/UserStore", UserStoreMessages.Update({ uid, active: false })); - return unit(); - }, - () => { - return new Error(`Unknown fingerprint id ${id}`); - } - ) + const fp = (await this.getEntity(id)).orUndefined(); + if (!fp) { + return new Error(`Unknown fingerprint id ${id}`); + } + const { uid } = await this.ask("actors://recapp-backend/UserStore", UserStoreMessages.GetByFingerprint(id)); + const updated = { ...fp, blocked: true }; + await this.storeEntity(updated); + this.updateSubscribers(updated); + if (uid) { + this.send("actors://recapp-backend/UserStore", UserStoreMessages.Update({ uid, active: false })); + } + return unit(); }, Unblock: async id => { - const mbFingerprint = await this.getEntity(id) - const {uid} = await this.ask("actors://recapp-backend/UserStore", UserStoreMessages.GetByFingerprint(id)); - return mbFingerprint.match( - fp => { - this.storeEntity({...fp, blocked: false}); - this.updateSubscribers({...fp, blocked: false}) - if (uid) - this.send("actors://recapp-backend/UserStore", UserStoreMessages.Update({ uid, active: true })); - return unit(); - }, - () => { - return new Error(`Unknown fingerprint id ${id}`); - } - ) + const fp = (await this.getEntity(id)).orUndefined(); + if (!fp) { + return new Error(`Unknown fingerprint id ${id}`); + } + const { uid } = await this.ask("actors://recapp-backend/UserStore", UserStoreMessages.GetByFingerprint(id)); + const updated = { ...fp, blocked: false }; + await this.storeEntity(updated); + this.updateSubscribers(updated); + if (uid) { + this.send("actors://recapp-backend/UserStore", UserStoreMessages.Update({ uid, active: true })); + } + return unit(); }, IncreaseCount: async ({fingerprint, userUid, initialQuiz}) => { - const mbFingerprint = await this.getEntity(fingerprint) - return mbFingerprint.match( - fp => { - this.storeEntity({...fp, usageCount: fp.usageCount + 1, lastSeen: toTimestamp(), userUid, initialQuiz: initialQuiz ?? fp.initialQuiz}); - this.updateSubscribers({...fp, usageCount: fp.usageCount + 1, lastSeen: toTimestamp(), userUid, initialQuiz: initialQuiz ?? fp.initialQuiz}) - return unit(); - }, - () => { - return new Error(`Unknown fingerprint id ${fingerprint}`); - } - ) + const fp = (await this.getEntity(fingerprint)).orUndefined(); + if (!fp) { + return new Error(`Unknown fingerprint id ${fingerprint}`); + } + const updated = { ...fp, usageCount: fp.usageCount + 1, lastSeen: toTimestamp(), userUid, initialQuiz: initialQuiz ?? fp.initialQuiz }; + await this.storeEntity(updated); + this.updateSubscribers(updated); + return unit(); }, GetMostRecent: async () => { const db = await this.connector.db(); diff --git a/packages/backend/src/actors/SessionStore.ts b/packages/backend/src/actors/SessionStore.ts index cdf6edb1..8eaebe99 100644 --- a/packages/backend/src/actors/SessionStore.ts +++ b/packages/backend/src/actors/SessionStore.ts @@ -47,16 +47,16 @@ export class SessionStore extends StoringActor { const result = await SessionStoreMessages.match>(message, { StoreSession: async session => { - this.state = await create(this.state, async draft => { - const currentSession = (await this.getEntity(session.uid)).orElse({} as Session); + const currentSession = (await this.getEntity(session.uid)).orElse({} as Session); + session.updated = toTimestamp(); + const newSession = { ...currentSession, ...session }; + this.state = create(this.state, draft => { if (session.actorSystem) { draft.clientIndex.set(session.actorSystem, session.uid); } - session.updated = toTimestamp(); - const newSession = { ...currentSession, ...session }; draft.cache.set(session.uid, newSession); - this.storeEntity(newSession); }); + await this.storeEntity(newSession); return unit(); }, CheckSession: async userId => { diff --git a/packages/backend/src/actors/StatisticsActor.ts b/packages/backend/src/actors/StatisticsActor.ts index deb8d1de..14f3ede5 100644 --- a/packages/backend/src/actors/StatisticsActor.ts +++ b/packages/backend/src/actors/StatisticsActor.ts @@ -243,7 +243,7 @@ export class StatisticsActor extends SubscribableActor< ); // console.log("STATS", stats); this.logger.info(`STATS stored quizId=${String(this.uid)}`); - this.storeEntity(stats); + await this.storeEntity(stats); for (const [subscriber] of this.state.collectionSubscribers) { this.send(subscriber, new StatisticsUpdateMessage(stats)); } diff --git a/packages/backend/src/actors/UserStore.ts b/packages/backend/src/actors/UserStore.ts index 82bc609e..478d9eca 100644 --- a/packages/backend/src/actors/UserStore.ts +++ b/packages/backend/src/actors/UserStore.ts @@ -353,26 +353,6 @@ export class UserStore extends SubscribableActor - ) - .then((session: Session) => { - session.role = newUser.role; - this.send(createActorUri("SessionStore"), SessionStoreMessages.StoreSession(session)); - }) - .catch((e: unknown) => { - this.logger.error( - `Failed to update session role for user ${String(newUser.uid)}: ` + - `${e instanceof Error ? e.stack : String(e)}` - ); - }); - } - for (const [subscriber, subscription] of this.state.collectionSubscribers) { this.send( subscriber, @@ -389,8 +369,30 @@ export class UserStore extends SubscribableActor newUser) .catch(error => error as Error); + if (!(storeResult instanceof Error) && oldUser.role !== newUser.role) { + try { + const sessionOrError = await this.ask( + createActorUri("SessionStore"), + SessionStoreMessages.GetSessionForUserId(newUser.uid) + ); + if (!(sessionOrError instanceof Error)) { + const session = sessionOrError as Session; + session.role = newUser.role; + await this.ask( + createActorUri("SessionStore"), + SessionStoreMessages.StoreSession(session) + ); + } + } catch (e: unknown) { + this.logger.error( + `Failed to update session role for user ${String(newUser.uid)}: ` + + `${e instanceof Error ? e.stack : String(e)}` + ); + } + } + return storeResult; }; } diff --git a/packages/frontend/src/actors/CurrentQuizActor.ts b/packages/frontend/src/actors/CurrentQuizActor.ts index 45dea603..ccfef043 100644 --- a/packages/frontend/src/actors/CurrentQuizActor.ts +++ b/packages/frontend/src/actors/CurrentQuizActor.ts @@ -598,7 +598,7 @@ export class CurrentQuizActor extends StatefulActor q !== id); return g; }); - await this.send( + await this.ask( actorUris.QuizActor, QuizActorMessages.Update({ uid: this.state.quiz.uid, groups }) ); diff --git a/packages/frontend/src/actors/SharingActor.ts b/packages/frontend/src/actors/SharingActor.ts index 66ef9bd2..94196251 100644 --- a/packages/frontend/src/actors/SharingActor.ts +++ b/packages/frontend/src/actors/SharingActor.ts @@ -76,6 +76,11 @@ export class SharingActor extends StatefulActor { + this.updateState(draft => { + draft.errors.push({ id: toId(v4()), queryNotFound: query }); + }); }); }, Clear: () => { diff --git a/packages/frontend/src/components/modals/ChangeNicknameModal.tsx b/packages/frontend/src/components/modals/ChangeNicknameModal.tsx index 3b526609..65ff6e2f 100644 --- a/packages/frontend/src/components/modals/ChangeNicknameModal.tsx +++ b/packages/frontend/src/components/modals/ChangeNicknameModal.tsx @@ -33,6 +33,7 @@ export const ChangeNicknameModal: React.FC = ({ show, defaultValue, onClo s .ask("actors://recapp-backend/UserStore", UserStoreMessages.IsNicknameUnique(newValue)) .then(result => !result && setError(i18n._("error-nickname-already-used"))) + .catch(() => { /* network error — skip uniqueness check */ }) ); } };