fix(delete): single signature for admin takedown — stop Amber collision hang (1.2.12)
Admin delete fired THREE signed events (kind:5 deletion, kind:1985 label, NIP-86
banevent). Each opens Amber, but Amber handles one approval at a time — the
extra background signs collided with the primary one on Android and hung the
whole delete ("taking longer than expected"). Now the admin path publishes only
the kind:5 deletion (one Amber approval) and hides locally (no signature); the
optional label/relay-ban are dropped from the interactive flow. Also raise the
awaited timeout 40s→120s to fit a real Amber round-trip. Bump to 1.2.12.
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
955a823b02
commit
52fd6f7a81
2 changed files with 21 additions and 58 deletions
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "skate-spots",
|
||||
"private": true,
|
||||
"version": "1.2.11",
|
||||
"version": "1.2.12",
|
||||
"type": "module",
|
||||
"homepage": "https://skate-spots.com",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -2,20 +2,18 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|||
import { useNostrPublish } from '@/hooks/useNostrPublish';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { isAdminPubkey } from '@/lib/admins';
|
||||
import {
|
||||
MODERATION_LABEL_NAMESPACE,
|
||||
MODERATION_LABEL_HIDDEN,
|
||||
} from '@/lib/types';
|
||||
import { addLocalHidden } from '@/lib/localModeration';
|
||||
import { banEventOnRelay } from '@/lib/relayAdmin';
|
||||
import type { NostrEvent } from '@nostrify/nostrify';
|
||||
|
||||
/**
|
||||
* Hard ceiling on how long the whole delete can run before we give up and
|
||||
* surface an error. Guarantees the UI spinner can NEVER hang indefinitely,
|
||||
* whatever the signer/relay does underneath.
|
||||
* Hard ceiling on how long the AWAITED part of the delete can run before we give
|
||||
* up. This must be generous enough for an Amber round-trip: the user has to
|
||||
* switch to Amber, approve, and switch back — which easily exceeds 40s if they
|
||||
* fumble. Too short a ceiling was surfacing "taking longer than expected" even
|
||||
* when Amber was about to succeed. The signer has its own 90s cap underneath, so
|
||||
* we align to that plus headroom.
|
||||
*/
|
||||
const DELETE_TIMEOUT_MS = 40_000;
|
||||
const DELETE_TIMEOUT_MS = 120_000;
|
||||
|
||||
function withHardTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
|
|
@ -113,56 +111,21 @@ export function useDeleteEntity() {
|
|||
tags: deletionTags,
|
||||
});
|
||||
|
||||
// STEP 2 (admin takedown only): record the target locally RIGHT NOW so the
|
||||
// app hides it on this device immediately — no waiting on a second signer
|
||||
// round-trip. Then fire the cross-device kind:1985 moderation label in the
|
||||
// BACKGROUND (not awaited): a second Amber approval, if needed, happens
|
||||
// after the dialog has already closed and never blocks the UI. This is the
|
||||
// fix for "stuck on Deleting…": the mutation resolves as soon as kind:5 is
|
||||
// published, regardless of the label.
|
||||
// STEP 2 (admin takedown only): hide the target LOCALLY right now so it
|
||||
// disappears from this device immediately. This needs NO signature.
|
||||
//
|
||||
// CRITICAL UX/BUG FIX: we deliberately do NOT fire additional signed
|
||||
// events here (the kind:1985 moderation label or the NIP-86 relay ban).
|
||||
// Each of those calls Amber, and Amber can only handle ONE approval at a
|
||||
// time — firing a second/third signer request while the first is still in
|
||||
// flight COLLIDES on Android and makes the whole delete hang ("taking
|
||||
// longer than expected", Amber juggling multiple prompts). The single
|
||||
// kind:5 deletion (step 1) already tells relays to drop the event, and the
|
||||
// local hide covers this device instantly. The optional label / relay-ban
|
||||
// can be performed separately from the relay moderator tools without
|
||||
// blocking or breaking the interactive delete.
|
||||
if (isAdmin && !isOwner) {
|
||||
addLocalHidden({ coord, id: event.id });
|
||||
|
||||
const labelTags: string[][] = [
|
||||
['L', MODERATION_LABEL_NAMESPACE],
|
||||
['l', MODERATION_LABEL_HIDDEN, MODERATION_LABEL_NAMESPACE],
|
||||
['a', coord],
|
||||
['e', event.id],
|
||||
['k', String(event.kind)],
|
||||
['p', event.pubkey],
|
||||
['alt', `SkateSpots admin takedown${reason?.trim() ? `: ${reason.trim()}` : ''}`],
|
||||
];
|
||||
// Intentionally NOT awaited — background best-effort.
|
||||
void publishEvent({
|
||||
kind: 1985,
|
||||
content: reason?.trim() || 'Removed by SkateSpots admin',
|
||||
tags: labelTags,
|
||||
}).catch((labelError) => {
|
||||
console.warn(
|
||||
'[useDeleteEntity] background moderation label failed (content is ' +
|
||||
'already hidden locally; kind:5 was published):',
|
||||
labelError,
|
||||
);
|
||||
});
|
||||
|
||||
// REAL takedown on our own relay (NIP-86 `banevent`). Because the admin
|
||||
// is the relay owner/moderator, goskatespots.nostr1.com honors this and
|
||||
// actually removes the event from its database — unlike a cross-author
|
||||
// kind:5 request. Also background/best-effort so it never blocks the UI;
|
||||
// this may prompt one more signer approval (NIP-98 auth event).
|
||||
if (user.signer) {
|
||||
void banEventOnRelay({
|
||||
signer: user.signer,
|
||||
eventId: event.id,
|
||||
reason: reason?.trim() || 'Removed by SkateSpots admin',
|
||||
}).catch((banError) => {
|
||||
console.warn(
|
||||
'[useDeleteEntity] relay banevent failed (content is hidden in-app ' +
|
||||
'regardless; you can retry from the relay moderator portal):',
|
||||
banError,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue