refactor(web): SpaceCard uses shared useSpaceJoin hook
This commit is contained in:
@@ -7,13 +7,13 @@ import { Mascot } from '../ui/Mascot';
|
|||||||
import { getSpaceGradient } from '../../utils/gradients';
|
import { getSpaceGradient } from '../../utils/gradients';
|
||||||
import { extractDominantColors, colorsToGradient } from '../../utils/colorExtractor';
|
import { extractDominantColors, colorsToGradient } from '../../utils/colorExtractor';
|
||||||
import { MemberListToggleButton } from '../layout/MemberListToggleButton';
|
import { MemberListToggleButton } from '../layout/MemberListToggleButton';
|
||||||
|
import { useSpaceJoin } from '../../hooks/useSpaceJoin';
|
||||||
|
|
||||||
export function ExplorePage() {
|
export function ExplorePage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const setCurrentSpace = useSpaceStore((s) => s.setCurrentSpace);
|
const setCurrentSpace = useSpaceStore((s) => s.setCurrentSpace);
|
||||||
|
|
||||||
const spaces = useExploreStore((s) => s.spaces);
|
const spaces = useExploreStore((s) => s.spaces);
|
||||||
const myRequests = useExploreStore((s) => s.myRequests);
|
|
||||||
const isLoading = useExploreStore((s) => s.isLoading);
|
const isLoading = useExploreStore((s) => s.isLoading);
|
||||||
const discoveryEnabled = useExploreStore((s) => s.discoveryEnabled);
|
const discoveryEnabled = useExploreStore((s) => s.discoveryEnabled);
|
||||||
const error = useExploreStore((s) => s.error);
|
const error = useExploreStore((s) => s.error);
|
||||||
@@ -150,7 +150,6 @@ export function ExplorePage() {
|
|||||||
<SpaceCard
|
<SpaceCard
|
||||||
key={`${space.id}:${space._instanceOrigin}`}
|
key={`${space.id}:${space._instanceOrigin}`}
|
||||||
space={space}
|
space={space}
|
||||||
isPending={myRequests.some(r => r.spaceId === space.id && r.status === 'pending')}
|
|
||||||
onJoinSuccess={handleJoinSuccess}
|
onJoinSuccess={handleJoinSuccess}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -187,7 +186,6 @@ export function ExplorePage() {
|
|||||||
<SpaceCard
|
<SpaceCard
|
||||||
key={`${space.id}:${space._instanceOrigin}`}
|
key={`${space.id}:${space._instanceOrigin}`}
|
||||||
space={space}
|
space={space}
|
||||||
isPending={false}
|
|
||||||
onJoinSuccess={handleJoinSuccess}
|
onJoinSuccess={handleJoinSuccess}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
@@ -204,26 +202,29 @@ export function ExplorePage() {
|
|||||||
|
|
||||||
function SpaceCard({
|
function SpaceCard({
|
||||||
space,
|
space,
|
||||||
isPending,
|
|
||||||
onJoinSuccess,
|
onJoinSuccess,
|
||||||
}: {
|
}: {
|
||||||
space: TaggedExploreSpace;
|
space: TaggedExploreSpace;
|
||||||
isPending: boolean;
|
|
||||||
onJoinSuccess: (spaceId: string) => void;
|
onJoinSuccess: (spaceId: string) => void;
|
||||||
}) {
|
}) {
|
||||||
const publicJoin = useExploreStore((s) => s.publicJoin);
|
const {
|
||||||
const requestJoin = useExploreStore((s) => s.requestJoin);
|
isJoined,
|
||||||
|
isPublic,
|
||||||
|
isPending,
|
||||||
|
joining,
|
||||||
|
joinError,
|
||||||
|
showRequestForm,
|
||||||
|
requestMessage,
|
||||||
|
setRequestMessage,
|
||||||
|
openRequestForm,
|
||||||
|
cancelRequestForm,
|
||||||
|
join,
|
||||||
|
sendRequest,
|
||||||
|
} = useSpaceJoin(space);
|
||||||
|
|
||||||
const [joining, setJoining] = useState(false);
|
|
||||||
const [showRequestForm, setShowRequestForm] = useState(false);
|
|
||||||
const [requestMessage, setRequestMessage] = useState('');
|
|
||||||
const [requestSent, setRequestSent] = useState(isPending);
|
|
||||||
const [joinError, setJoinError] = useState('');
|
|
||||||
const [iconGradient, setIconGradient] = useState<string | null>(null);
|
const [iconGradient, setIconGradient] = useState<string | null>(null);
|
||||||
|
|
||||||
const fallbackGradient = getSpaceGradient(space.id, space.name, space.avatarColor).gradient;
|
const fallbackGradient = getSpaceGradient(space.id, space.name, space.avatarColor).gradient;
|
||||||
const isPublic = space.visibility === 'public';
|
|
||||||
const isJoined = space.joined === true;
|
|
||||||
const originLabel = space._instanceOrigin
|
const originLabel = space._instanceOrigin
|
||||||
? (() => { try { return new URL(space._instanceOrigin).host; } catch { return space._instanceOrigin; } })()
|
? (() => { try { return new URL(space._instanceOrigin).host; } catch { return space._instanceOrigin; } })()
|
||||||
: null;
|
: null;
|
||||||
@@ -248,29 +249,8 @@ function SpaceCard({
|
|||||||
}, [iconUrl, bannerUrl]);
|
}, [iconUrl, bannerUrl]);
|
||||||
|
|
||||||
const handlePublicJoin = async () => {
|
const handlePublicJoin = async () => {
|
||||||
setJoining(true);
|
const full = await join();
|
||||||
setJoinError('');
|
if (full) onJoinSuccess(full.id);
|
||||||
try {
|
|
||||||
const fullSpace = await publicJoin(space);
|
|
||||||
onJoinSuccess(fullSpace.id);
|
|
||||||
} catch (err) {
|
|
||||||
setJoinError(err instanceof Error ? err.message : 'Failed to join');
|
|
||||||
setJoining(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRequestJoin = async () => {
|
|
||||||
setJoining(true);
|
|
||||||
setJoinError('');
|
|
||||||
try {
|
|
||||||
await requestJoin(space, requestMessage.trim() || undefined);
|
|
||||||
setRequestSent(true);
|
|
||||||
setShowRequestForm(false);
|
|
||||||
} catch (err) {
|
|
||||||
setJoinError(err instanceof Error ? err.message : 'Failed to send request');
|
|
||||||
} finally {
|
|
||||||
setJoining(false);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleViewSpace = () => {
|
const handleViewSpace = () => {
|
||||||
@@ -397,7 +377,7 @@ function SpaceCard({
|
|||||||
'Join Space'
|
'Join Space'
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
) : requestSent ? (
|
) : isPending ? (
|
||||||
<button
|
<button
|
||||||
disabled
|
disabled
|
||||||
className="w-full py-2 bg-interactive-muted text-txt-tertiary text-sm font-medium rounded cursor-default"
|
className="w-full py-2 bg-interactive-muted text-txt-tertiary text-sm font-medium rounded cursor-default"
|
||||||
@@ -415,14 +395,14 @@ function SpaceCard({
|
|||||||
/>
|
/>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button
|
<button
|
||||||
onClick={handleRequestJoin}
|
onClick={sendRequest}
|
||||||
disabled={joining}
|
disabled={joining}
|
||||||
className="flex-1 py-1.5 bg-accent-amber hover:bg-accent-amber/80 text-[#13131a] text-sm font-medium rounded transition-colors disabled:opacity-50"
|
className="flex-1 py-1.5 bg-accent-amber hover:bg-accent-amber/80 text-[#13131a] text-sm font-medium rounded transition-colors disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{joining ? 'Sending...' : 'Send Request'}
|
{joining ? 'Sending...' : 'Send Request'}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowRequestForm(false)}
|
onClick={cancelRequestForm}
|
||||||
className="px-3 py-1.5 text-sm text-txt-tertiary hover:text-txt-secondary transition-colors"
|
className="px-3 py-1.5 text-sm text-txt-tertiary hover:text-txt-secondary transition-colors"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
@@ -431,7 +411,7 @@ function SpaceCard({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
onClick={() => setShowRequestForm(true)}
|
onClick={openRequestForm}
|
||||||
className="w-full py-2 bg-accent-amber/20 hover:bg-accent-amber/30 text-accent-amber text-sm font-medium rounded transition-colors"
|
className="w-full py-2 bg-accent-amber/20 hover:bg-accent-amber/30 text-accent-amber text-sm font-medium rounded transition-colors"
|
||||||
>
|
>
|
||||||
Request to Join
|
Request to Join
|
||||||
|
|||||||
Reference in New Issue
Block a user