diff --git a/web/components/profile/profile-header.tsx b/web/components/profile/profile-header.tsx index 05a4ede..94d5652 100644 --- a/web/components/profile/profile-header.tsx +++ b/web/components/profile/profile-header.tsx @@ -59,7 +59,8 @@ export default function ProfileHeader(props: { {currentUser && isCurrentUser && disabled && -
{t('profile.header.disabled_notice', 'You disabled your profile, so no one else can access it.')}
} +
{t('profile.header.disabled_notice', 'You disabled your profile, so no one else can access it.')}
} {/*{!isCurrentUser && }*/} @@ -83,73 +84,73 @@ export default function ProfileHeader(props: { username={user.username} /> - + - ) : ( diff --git a/web/components/settings/hidden-profiles-modal.tsx b/web/components/settings/hidden-profiles-modal.tsx index 0e95113..3428d41 100644 --- a/web/components/settings/hidden-profiles-modal.tsx +++ b/web/components/settings/hidden-profiles-modal.tsx @@ -10,15 +10,7 @@ import toast from 'react-hot-toast' import {useT} from 'web/lib/locale' import clsx from "clsx"; import Link from "next/link"; -import {CompassLoadingIndicator} from "web/components/widgets/loading-indicator"; - -type HiddenUser = { - id: string - name: string - username: string - avatarUrl?: string | null - createdTime?: string -} +import {useHiddenProfiles} from "web/hooks/use-hidden-profiles"; export function HiddenProfilesModal(props: { open: boolean @@ -26,32 +18,14 @@ export function HiddenProfilesModal(props: { }) { const {open, setOpen} = props const t = useT() - const [loading, setLoading] = useState(false) - const [error, setError] = useState(null) - const [hidden, setHidden] = useState(null) const [busyIds, setBusyIds] = useState>({}) + const {hiddenProfiles, refreshHiddenProfiles} = useHiddenProfiles() - const empty = useMemo(() => (hidden ? hidden.length === 0 : false), [hidden]) + const empty = useMemo(() => (hiddenProfiles ? hiddenProfiles.length === 0 : false), [hiddenProfiles]) useEffect(() => { if (!open) return - let alive = true - setLoading(true) - setError(null) - api('get-hidden-profiles', {limit: 200, offset: 0}) - .then((res) => { - if (!alive) return - setHidden(res.hidden) - }) - .catch((e) => { - console.error('Failed to load hidden profiles', e) - if (!alive) return - setError(t('settings.hidden_profiles.load_error', 'Failed to load hidden profiles.')) - }) - .finally(() => alive && setLoading(false)) - return () => { - alive = false - } + refreshHiddenProfiles() }, [open]) const unhide = async (userId: string) => { @@ -59,12 +33,12 @@ export function HiddenProfilesModal(props: { setBusyIds((b) => ({...b, [userId]: true})) try { await api('unhide-profile', {hiddenUserId: userId}) - setHidden((list) => (list ? list.filter((u) => u.id !== userId) : list)) + refreshHiddenProfiles() } catch (e) { console.error('Failed to unhide profile', e) toast.error(t('settings.hidden_profiles.unhide_failed', 'Failed to unhide')) } finally { - setBusyIds((b) => ({...b, [userId]: false})) + // setBusyIds((b) => ({...b, [userId]: false})) } } @@ -74,14 +48,9 @@ export function HiddenProfilesModal(props: { {t('settings.hidden_profiles.title', "Profiles you've hidden")} - {!loading && hidden && hidden.length > 0 &&

- {t('settings.hidden_profiles.description', "These people don't appear in your search results.")} -

} - {loading && } - {error &&
{error}
} - {!loading && hidden && hidden.length > 0 && ( + {hiddenProfiles && hiddenProfiles.length > 0 && ( - {hidden.map((u) => ( + {hiddenProfiles.map((u) => ( )} - {!loading && empty && ( + {empty && (
{t( 'settings.hidden_profiles.empty', diff --git a/web/components/widgets/hide-profile-button.tsx b/web/components/widgets/hide-profile-button.tsx index 2334fa9..f3fb2c8 100644 --- a/web/components/widgets/hide-profile-button.tsx +++ b/web/components/widgets/hide-profile-button.tsx @@ -1,9 +1,10 @@ import clsx from 'clsx' -import {useState} from 'react' +import {useMemo, useState} from 'react' import {EyeIcon, EyeOffIcon} from '@heroicons/react/outline' import {Tooltip} from 'web/components/widgets/tooltip' import {api} from 'web/lib/api' import {useT} from 'web/lib/locale' +import {useHiddenProfiles} from "web/hooks/use-hidden-profiles"; export type HideProfileButtonProps = { hiddenUserId: string @@ -28,17 +29,34 @@ export function HideProfileButton(props: HideProfileButtonProps) { const t = useT() const [submitting, setSubmitting] = useState(false) - const [clicked, setClicked] = useState(false) + const {hiddenProfiles, refreshHiddenProfiles} = useHiddenProfiles() + const [optimisticHidden, setOptimisticHidden] = useState(undefined) + const hidden = useMemo(() => { + if (optimisticHidden !== undefined) return optimisticHidden + return hiddenProfiles?.some((u) => u.id === hiddenUserId) ?? false + }, [hiddenProfiles, hiddenUserId, optimisticHidden]) const onClick = async (e: React.MouseEvent) => { e.preventDefault() if (stopPropagation) e.stopPropagation() if (submitting) return setSubmitting(true) - setClicked(true) + + // Optimistically update hidden state + setOptimisticHidden(!hidden) + try { - await api('hide-profile', {hiddenUserId}) + if (hidden) { + await api('unhide-profile', {hiddenUserId}) + } else { + await api('hide-profile', {hiddenUserId}) + } + refreshHiddenProfiles() onHidden?.(hiddenUserId) + } catch (e) { + console.error('Failed to toggle hide profile', e) + // Revert optimistic update on failure + setOptimisticHidden(hidden) } finally { setSubmitting(false) } @@ -46,21 +64,20 @@ export function HideProfileButton(props: HideProfileButtonProps) { return ( diff --git a/web/hooks/use-hidden-profiles.ts b/web/hooks/use-hidden-profiles.ts new file mode 100644 index 0000000..d683e9e --- /dev/null +++ b/web/hooks/use-hidden-profiles.ts @@ -0,0 +1,37 @@ +import {useUser} from 'web/hooks/use-user' +import {useEffect} from 'react' +import {usePersistentLocalState} from 'web/hooks/use-persistent-local-state' +import {api} from "web/lib/api"; + +type HiddenUser = { + id: string + name: string + username: string + avatarUrl?: string | null + createdTime?: string +} + +export const useHiddenProfiles = () => { + const user = useUser() + const [hiddenProfiles, setHiddenProfiles] = usePersistentLocalState< + HiddenUser[] | undefined | null + >(undefined, `hidden-ids-${user?.id}`) + + const refreshHiddenProfiles = () => { + if (user) { + api('get-hidden-profiles', {limit: 200, offset: 0}) + .then((res) => { + setHiddenProfiles(res.hidden) + }) + .catch((e) => { + console.error('Failed to load hidden profiles', e) + }) + } + } + + useEffect(() => { + refreshHiddenProfiles() + }, [user?.id]) + + return {hiddenProfiles, refreshHiddenProfiles} +}