From 20d463ff2f5a112473f75a21595b3d89b8dfc0b0 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Mon, 18 Mar 2024 12:46:28 -0700 Subject: 3p moderation services [WIP] (#2550) * Add modservice screen and profile-header-card * Drop the guidelines for now * Remove ununsed constants * Add label & label group descriptions * Not found state * Reorg, add icon * Subheader * Header * Complete header * Clean up * Add all groups * Fix scroll view * Dialogs side quest * Remove log * Add (WIP) debug mod page * Dialog solution * Add note * Clean up and reorganize localized moderation strings * Memoize * Add example * Add first ReportDialog screen * Report dialog step 2 * Submit * Integrate updates * Move moderation screen * Migrate buttons * Migrate everything * Rough sketch * Fix types * Update atoms values * Abstract ModerationServiceCard * Hook up data to settings page * Handle subscription * Rough enablement * Rough enablement * Some validation, fixes * More work on the mod debug screen * Hook up data * Update invalidation * Hook up data to ReportDialog * Fix native error * Refactor/rewrite the entire moderation-application system * Fix toggles * Add copyright and other option to report * Handle reports on profile vs content * Little cleanup * Get post hiding back in gear * Better loading flow on Mod screen * Clean up Mod screen * Clean up ProfileMod screen * Handle muting correctly * Update enablement on ProfileMod screen * Improve Moderation screen and dialog * Styling, handle disabled labelers * Rework list of labels on own content * Use moderateNotification() * ReportDialog updates * Fix button overflow * Simplify the ProfileModerationService ui * Mod screen design * Move moderation card from the profile header to a tab * Small tweaks to the moderation screen * Enable toggle on mod page * Add notifs to debugmod and dont filter notifs from followed users * Add moderator-service profile view * Wire up more of the modservice data to profiles * A bunch of speculative non-working UI * Cleanup: delete old code * Update ModerationDetailsDialog * Update ReportDialog * Update LabelsOnMe dialog * Handle ReportDialog load better * Rename LabelsOnMeDialog, fix close * Experiment to put labeling under a tab of a normal profile * Moderator variation of profile * Remove dead code and start moving toward latest modsdk * Remove a bunch of now-dead label strings * Update ModDebug to be a bit more intuitive and support custom labels * Minor ui tweaks * Improve consistency of display name blurring * Fix profile-card warning rendering * More debugmod UI tuning * Update to use new labeler semantics * Delete some dead code and do some refactoring * Update profile to pull from labeler definition * Implement new label config controls (wip) * Tweak ui * Implement preference controls on labelers * Rework label pref ui * Get moderation screen working * Add asyncstorage query persistence * Implement label handling * Small cleanup * Implement Likes dialog * Fix: remove text outside of text element * Cleanup * Fix likes dialog on mobile * Implement the label appeal flow * Get report flow working again with temporarily fixed report options * Update onboarding * Enforce limit of ten labeler subscriptions * Fix type errors * Fix lint errors * Improve types of RQ * Some work on Likes dialog, needs discussion * Bit of ReportDialog cleanup * Replace non-single-path SVG * Update nudity descriptions * Update to use new sdk updates * Add adult-content-enabled behavior to label config * Use the default setting of custom labels * Handle global moderation label prefs with the global settings * Fix missing postAuthor * Fix empty moderation page * Add mutewords control back to Mod screen * Tweak adult setting styles * Remove deprecated global labels * Handle underage users on mod screen * Adjust font sizes * Swap in RichText * Like button improvements * Tweaks to Labeler profile * Design tweaks for mod pref dialog * Add tertiary button color * Switch moderation UIs to tertiary color * Update mutewords and hiddenposts to use the new sdk * Add test-environment mod authority * Switch 'gore' to 'graphic-media' * Move nudity out of the adult content control * Remove focus styles from buttons - let the browser behavior handle it * Fixes to the adult content age-gating in moderaiton * Ditch tertiary button color, lighten secondary button * Fix some colors * Remove focused overrides from toggles * Liked by screen * Rework the moderationlabelpref * Fix optimistic like * Cleanup * Change how onboarding handles adult content enabled/disabled * Add special handling of the mod authorities * Tweaks * Update the default labeler avatar to a shield * Add route to go server * Avoid dups due to bad config * Fix attrs * Fix: dont try to detect link/label mismatches on post meta * Correctly show the label behavior when adult content is disabled * Readd the local hiddenPosts handling * WIP * Fix bad merge * Conten hider design tweaks * Fix text string breakage * Adjust source text in ContentHider * Fix link bug * Design tweaks to ContentHider and ModDetailsDialog * Adjust spacing of inform badges * Adjust spacing of embeds in posts * Style tweaks to post/profile alerts * Labels on me and dialog * Remove bad focus styles from post dropdown * Better spacing solution * Tune moderation UIs * Moderation UI tweaks for mobile * Move labelers query on Mod screen * Update to use new SDK appLabelers semantics * Implement report submission * Replace the report modal entirely with the report dialog * Add @ to mod details dialog handle * Bump SDK package * Remove silly type * Add to AWS build CI * Fix ToggleButton overflow * Clean up ModServiceCard, rename to LabelingServiceCard * Hackfix to translate gore labels to graphic-media * Tune content hider sizing on web desktop * Handle self labels * Fix spacing below text-only posts * Fix: send appeals to the right labeler * Give mod page links interactive states * Fix references * Remove focus handling * Remove remnant * Remove the like count from the subscribed labeler listing * Bump @atproto/api@0.11.1 * Remove extra @ * Fix: persist labels to local storage to reduce coverage gaps * update dipendencies * revert dipendencies * Add some explainers on how blocking affects labelers * Tweak copy * Fix underline color in header * Fix profile menu * Handle card overflow * Remove metrics from header * Mute 'account' not 'user' * Show metrics if self * Show the labels tab on logged out view * Fix bad merge * Use purple theming on labelers * Tighten space on LabelerCard * Set staleTime to 6hrs for labeler details * Memoize the memoizers * Drop staleTime to 60s * Move label defs into a context to reduce recomputes * Submit view tweaks * Move labeler fetch below auth * Mitigation: hardcode the bluesky moderation labeler name * Bump sdk * Add missing translated string Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Add missing translated string Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Hailey's fix for incorrect profile tabs Co-authored-by: Hailey * Feedback * Fix borders, add bottom space * Hailey's fix pt 2 Co-authored-by: Hailey * Fix post tabs * Integrate feedback pt 1 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 2 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 3 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 4 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 5 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 6 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 7 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Integrate feedback pt 8 Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> * Format * Integrate new bday modal * Use public agent for getServices * Update casing --------- Co-authored-by: Eric Bailey Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> Co-authored-by: Hailey --- .../Profile/Header/ProfileHeaderLabeler.tsx | 329 +++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 src/screens/Profile/Header/ProfileHeaderLabeler.tsx (limited to 'src/screens/Profile/Header/ProfileHeaderLabeler.tsx') diff --git a/src/screens/Profile/Header/ProfileHeaderLabeler.tsx b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx new file mode 100644 index 000000000..6722ed09b --- /dev/null +++ b/src/screens/Profile/Header/ProfileHeaderLabeler.tsx @@ -0,0 +1,329 @@ +import React, {memo, useMemo} from 'react' +import {View} from 'react-native' +import { + AppBskyActorDefs, + AppBskyLabelerDefs, + ModerationOpts, + moderateProfile, + RichText as RichTextAPI, +} from '@atproto/api' +import {Trans, msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {RichText} from '#/components/RichText' +import {useModalControls} from '#/state/modals' +import {usePreferencesQuery} from '#/state/queries/preferences' +import {useAnalytics} from 'lib/analytics/analytics' +import {useSession} from '#/state/session' +import {Shadow} from '#/state/cache/types' +import {useProfileShadow} from 'state/cache/profile-shadow' +import {useLabelerSubscriptionMutation} from '#/state/queries/labeler' +import {useLikeMutation, useUnlikeMutation} from '#/state/queries/like' +import {logger} from '#/logger' +import {Haptics} from '#/lib/haptics' +import {pluralize} from '#/lib/strings/helpers' +import {isAppLabeler} from '#/lib/moderation' + +import {atoms as a, useTheme, tokens} from '#/alf' +import {Button, ButtonText} from '#/components/Button' +import {Text} from '#/components/Typography' +import * as Toast from '#/view/com/util/Toast' +import {ProfileHeaderShell} from './Shell' +import {ProfileMenu} from '#/view/com/profile/ProfileMenu' +import {ProfileHeaderDisplayName} from './DisplayName' +import {ProfileHeaderHandle} from './Handle' +import {ProfileHeaderMetrics} from './Metrics' +import { + Heart2_Stroke2_Corner0_Rounded as Heart, + Heart2_Filled_Stroke2_Corner0_Rounded as HeartFilled, +} from '#/components/icons/Heart2' +import {DialogOuterProps} from '#/components/Dialog' +import * as Prompt from '#/components/Prompt' +import {Link} from '#/components/Link' + +interface Props { + profile: AppBskyActorDefs.ProfileViewDetailed + labeler: AppBskyLabelerDefs.LabelerViewDetailed + descriptionRT: RichTextAPI | null + moderationOpts: ModerationOpts + hideBackButton?: boolean + isPlaceholderProfile?: boolean +} + +let ProfileHeaderLabeler = ({ + profile: profileUnshadowed, + labeler, + descriptionRT, + moderationOpts, + hideBackButton = false, + isPlaceholderProfile, +}: Props): React.ReactNode => { + const profile: Shadow = + useProfileShadow(profileUnshadowed) + const t = useTheme() + const {_} = useLingui() + const {currentAccount, hasSession} = useSession() + const {openModal} = useModalControls() + const {track} = useAnalytics() + const cantSubscribePrompt = Prompt.usePromptControl() + const isSelf = currentAccount?.did === profile.did + + const moderation = useMemo( + () => moderateProfile(profile, moderationOpts), + [profile, moderationOpts], + ) + const {data: preferences} = usePreferencesQuery() + const {mutateAsync: toggleSubscription, variables} = + useLabelerSubscriptionMutation() + const isSubscribed = + variables?.subscribe ?? + preferences?.moderationPrefs.labelers.find(l => l.did === profile.did) + const canSubscribe = + isSubscribed || + (preferences ? preferences?.moderationPrefs.labelers.length < 9 : false) + const {mutateAsync: likeMod, isPending: isLikePending} = useLikeMutation() + const {mutateAsync: unlikeMod, isPending: isUnlikePending} = + useUnlikeMutation() + const [likeUri, setLikeUri] = React.useState( + labeler.viewer?.like || '', + ) + const [likeCount, setLikeCount] = React.useState(labeler.likeCount || 0) + + const onToggleLiked = React.useCallback(async () => { + if (!labeler) { + return + } + try { + Haptics.default() + + if (likeUri) { + await unlikeMod({uri: likeUri}) + track('CustomFeed:Unlike') + setLikeCount(c => c - 1) + setLikeUri('') + } else { + const res = await likeMod({uri: labeler.uri, cid: labeler.cid}) + track('CustomFeed:Like') + setLikeCount(c => c + 1) + setLikeUri(res.uri) + } + } catch (e: any) { + Toast.show( + _( + msg`There was an an issue contacting the server, please check your internet connection and try again.`, + ), + ) + logger.error(`Failed to toggle labeler like`, {message: e.message}) + } + }, [labeler, likeUri, likeMod, unlikeMod, track, _]) + + const onPressEditProfile = React.useCallback(() => { + track('ProfileHeader:EditProfileButtonClicked') + openModal({ + name: 'edit-profile', + profile, + }) + }, [track, openModal, profile]) + + const onPressSubscribe = React.useCallback(async () => { + if (!canSubscribe) { + cantSubscribePrompt.open() + return + } + try { + await toggleSubscription({ + did: profile.did, + subscribe: !isSubscribed, + }) + } catch (e: any) { + // setSubscriptionError(e.message) + logger.error(`Failed to subscribe to labeler`, {message: e.message}) + } + }, [ + toggleSubscription, + isSubscribed, + profile, + canSubscribe, + cantSubscribePrompt, + ]) + + const isMe = React.useMemo( + () => currentAccount?.did === profile.did, + [currentAccount, profile], + ) + + return ( + + + + {isMe ? ( + + ) : !isAppLabeler(profile.did) ? ( + <> + + + ) : null} + + + + + + + {!isPlaceholderProfile && ( + <> + {isSelf && } + {descriptionRT && !moderation.ui('profileView').blur ? ( + + + + ) : undefined} + {!isAppLabeler(profile.did) && ( + + + + {typeof likeCount === 'number' && ( + + {({hovered, focused, pressed}) => ( + + + Liked by {likeCount} {pluralize(likeCount, 'user')} + + + )} + + )} + + )} + + )} + + + + ) +} +ProfileHeaderLabeler = memo(ProfileHeaderLabeler) +export {ProfileHeaderLabeler} + +function CantSubscribePrompt({ + control, +}: { + control: DialogOuterProps['control'] +}) { + return ( + + Unable to subscribe + + + We're sorry! You can only subscribe to ten labelers, and you've + reached your limit of ten. + + + + OK + + + ) +} -- cgit 1.4.1