Report a Join on an event without an attendees list as not recorded, and trim redundant comments

This commit is contained in:
Sylvain Duchesne
2026-11-06 23:21:38 +01:00
parent 5540d361c7
commit 9169a5817c
4 changed files with 46 additions and 45 deletions
@@ -54,6 +54,7 @@ export function EventDetailScreen() {
const registration = eventId ? getEventRegistration(eventId) : undefined;
const registered = registration?.registered ?? false;
const [confirmingLeave, setConfirmingLeave] = useState(false);
const [joining, setJoining] = useState(false);
const [leaving, setLeaving] = useState(false);
const handleToggleMeetingPoint = (id: string, _title: string, willJoin: boolean) => {
@@ -71,11 +72,14 @@ export function EventDetailScreen() {
};
const handleJoinEvent = () => {
if (!eventId) return;
void Promise.resolve(joinEvent(eventId)).catch((err: unknown) => {
console.error('[EventDetail] event sign-up failed:', err);
showToast("L'inscription à l'événement n'a pas pu être enregistrée", 'error');
});
if (!eventId || joining) return;
setJoining(true);
Promise.resolve(joinEvent(eventId))
.catch((err: unknown) => {
console.error('[EventDetail] event sign-up failed:', err);
showToast("L'inscription à l'événement n'a pas pu être enregistrée", 'error');
})
.finally(() => setJoining(false));
};
const withdrawFromEvent = () => {
@@ -92,8 +96,6 @@ export function EventDetailScreen() {
});
};
// Withdrawing from an event also leaves its meeting points: ask first when
// there are any, otherwise withdraw at once.
const handleLeaveEvent = () => {
if (registration && registration.meetingPointIds.length > 0) setConfirmingLeave(true);
else withdrawFromEvent();
@@ -217,7 +219,7 @@ export function EventDetailScreen() {
</Button>
</>
) : (
<Button variant="primary" onClick={handleJoinEvent} style={{ padding: '12px 0' }}>
<Button variant="primary" onClick={handleJoinEvent} disabled={joining} style={{ padding: '12px 0' }}>
S'inscrire à l'événement
</Button>
)}
+16 -24
View File
@@ -205,6 +205,18 @@ interface ContactKeys {
}
/** The memo key of one contact's member URN at one event. */
/** The event a joined target is anchored to: its own event for a meeting point, itself for an event. */
function anchorEventIdOf(bindings: RefineBackend['bindings'], targetId: string, record: unknown): string | undefined {
if (bindings.meetingPoint.isInstance?.(record) ?? false) {
try {
return bindings.meetingPoint.toDomain(record).eventId || undefined;
} catch {
return undefined;
}
}
return bindings.event.isInstance?.(record) ?? true ? targetId : undefined;
}
function memberUrnKey(contactId: string, eventId: string, secret: string): string {
return `${contactId}\n${eventId}\n${secret}`;
}
@@ -464,17 +476,8 @@ function useRefineData(backend: RefineBackend): FestipodDataContextValue {
for (const mpId of joinedForeignMeetingPointIds) {
const record = foreignMeetingPointsRead.records.get(mpId);
if (record === undefined) continue;
// A joined target that reads as an event is a direct sign-up: its own anchor.
if (!(bindings.meetingPoint.isInstance?.(record) ?? false)) {
if (bindings.event.isInstance?.(record) ?? true) ids.add(mpId);
continue;
}
try {
const eventId = bindings.meetingPoint.toDomain(record).eventId;
if (eventId) ids.add(eventId);
} catch {
// Unreadable by the binding — the pick below drops the record too.
}
const eventId = anchorEventIdOf(bindings, mpId, record);
if (eventId) ids.add(eventId);
}
return [...ids].filter(id => !ownEventIds.has(id));
}, [joinedForeignMeetingPointIds, foreignMeetingPointsRead.records, bindings, ownEventIds]);
@@ -852,17 +855,8 @@ function useRefineData(backend: RefineBackend): FestipodDataContextValue {
for (const uri of contactMpUris) {
const record = contactMpsRead.records.get(uri);
if (record === undefined) continue;
// A listed target that reads as an event is a direct sign-up: its own anchor.
if (!(bindings.meetingPoint.isInstance?.(record) ?? false)) {
if (bindings.event.isInstance?.(record) ?? true) ids.set(uri, uri);
continue;
}
try {
const eventId = bindings.meetingPoint.toDomain(record).eventId;
if (eventId) ids.set(uri, eventId);
} catch {
// Not a readable meeting point — the binding drops it, as everywhere.
}
const eventId = anchorEventIdOf(bindings, uri, record);
if (eventId) ids.set(uri, eventId);
}
return ids;
}, [contactMpUris, contactMpsRead.records, bindings]);
@@ -1248,8 +1242,6 @@ function useRefineData(backend: RefineBackend): FestipodDataContextValue {
afterMembershipAct(event.id, 'join');
}, [logPrefix, allEvents, session, myTargetIds, ensureParticipationSecret, afterMembershipAct]);
// One Leave per held target, in parallel; each accepted one is followed, the
// refused ones are named — the status keeps following the data.
const leaveEvent = useCallback(async (eventId: string) => {
if (!allEvents.some(e => e.id === eventId) || !session) {
throw new Error(`leaveEvent refused: ${eventId} is not in the read set (or there is no session).`);