fix(web): code review round 3 -- UI bugs and remaining design fidelity
- FileAttachment never surfaced a failed authenticated download -- the card looked identically clickable whether it worked or not, and clicking while broken silently no-op'd. Added an error state/retry (useAuthedBlobUrl gained a retry() escape hatch for this). ImageAttachment/AudioAttachment already handled this; only the file card didn't. - --shadow-btn was one token serving two different specs: round 2's fix (0 6px 16px, from the 50px auth CTAs) actually regressed the 36px messaging send button (ConversationView/VoiceRecorder), which needs 0 4px 12px per 02-messages.dc.html. Split into --shadow-btn / --shadow-btn-sm. - loadOlder's scroll-restore guard compared topicId by value, which false-positives on an A->B->A round trip within one request's lifetime (same string, but the DOM was torn down and rebuilt by the intervening open/close). Switched to a generation counter, same pattern as topics.ts's leave-race fix. - errorToast wasn't included in ConversationView/DetailColumn's topicId-reset effects -- a failed action's toast could keep showing over a conversation/contact switched to afterward. Also switched those effects (and MessagesLayout's) from useEffect to useLayoutEffect -- since none of these components remount on topicId change, a post-paint effect could let one frame render with the previous topicId's stale UI still showing. - VoiceRecorder never listened for MediaRecorder's own onerror -- a mid-recording failure left the "录音中" timer ticking forever with no indication the recording had already died. - VerificationCodeButton's phone-change countdown reset (round 2) only cleared the button's own state; RegisterPage/ResetPasswordPage's challengeId was still tied to whatever phone the code was actually issued for, with nothing cross-checking it against a since-edited phone number. - Agreement's checkbox used bare accent-color on a native input, which only tints the checked fill and leaves size/radius/border to the browser/OS; replaced with the spec's exact 16x16 border-radius:4px box + check glyph. - Added a catch-all route (App.tsx), TenantFeedbackPage image-picker validation feedback + corrected remove-button style, and a handful of smaller token/spacing corrections against screens/*.dc.html (NavRail font-weight, LoginPage/TenantEntryPage logo size+shadow+color, ChatListColumn row padding/icon size, --shadow-modal alpha). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
8ec74dffcc
commit
72b7b79caf
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useLayoutEffect, useState } from 'react';
|
||||
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
@@ -56,10 +56,14 @@ export function DetailColumn({ topicId, peerName, onClose, onOpenHistory }: Deta
|
||||
|
||||
// DetailColumn is reused across every contact when docked (no `key={topicId}`, same instance),
|
||||
// so without this an in-progress remark edit for the PREVIOUS contact stays open and could be
|
||||
// saved against the NEW one.
|
||||
useEffect(() => {
|
||||
// saved against the NEW one, and a leftover error toast would keep showing over the new contact
|
||||
// misattributing the failure. useLayoutEffect (not useEffect) so this resolves before paint --
|
||||
// this component never remounts on topicId change, so a post-paint effect can let one frame
|
||||
// render with the previous contact's stale edit box still open.
|
||||
useLayoutEffect(() => {
|
||||
setRemarkEditing(false);
|
||||
setRemarkDraft('');
|
||||
setErrorToast('');
|
||||
}, [topicId]);
|
||||
|
||||
const friendsQuery = useQuery({ queryKey: ['friends'], queryFn: () => fetchAllPages((cursor) => getFriends('', cursor)) });
|
||||
@@ -98,7 +102,7 @@ export function DetailColumn({ topicId, peerName, onClose, onOpenHistory }: Deta
|
||||
});
|
||||
|
||||
const blocksQuery = useQuery({ queryKey: ['blocks'], queryFn: () => fetchAllPages((cursor) => getBlocks(cursor)) });
|
||||
const isBlocked = !!blocksQuery.data?.some((b) => b.user_id === topicId);
|
||||
const isBlocked = !!blocksQuery.data?.some((b) => b.user.user_id === topicId);
|
||||
|
||||
const deleteFriendMutation = useMutation({
|
||||
mutationFn: () => deleteFriend(topicId),
|
||||
|
||||
Reference in New Issue
Block a user