diff options
author | Paul Frazee <pfrazee@gmail.com> | 2024-03-18 12:46:28 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-03-18 12:46:28 -0700 |
commit | 20d463ff2f5a112473f75a21595b3d89b8dfc0b0 (patch) | |
tree | 371aa26309374de3b9deef001c99f41bff1057d1 /src/view/screens/Profile.tsx | |
parent | d5ebbeb3fc2802e80f55162996b6baabbf764f9c (diff) | |
download | voidsky-20d463ff2f5a112473f75a21595b3d89b8dfc0b0.tar.zst |
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 <me@haileyok.com> * Feedback * Fix borders, add bottom space * Hailey's fix pt 2 Co-authored-by: Hailey <me@haileyok.com> * 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 <git@esb.lol> Co-authored-by: Takayuki KUSANO <65759+tkusano@users.noreply.github.com> Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/view/screens/Profile.tsx')
-rw-r--r-- | src/view/screens/Profile.tsx | 253 |
1 files changed, 125 insertions, 128 deletions
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx index b30b4491b..d5a46c5c9 100644 --- a/src/view/screens/Profile.tsx +++ b/src/view/screens/Profile.tsx @@ -1,5 +1,5 @@ import React, {useMemo} from 'react' -import {StyleSheet, View} from 'react-native' +import {StyleSheet} from 'react-native' import {useFocusEffect} from '@react-navigation/native' import { AppBskyActorDefs, @@ -7,48 +7,39 @@ import { ModerationOpts, RichText as RichTextAPI, } from '@atproto/api' -import {msg, Trans} from '@lingui/macro' +import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' import {CenteredView} from '../com/util/Views' import {ListRef} from '../com/util/List' -import {ScreenHider} from 'view/com/util/moderation/ScreenHider' -import {Feed} from 'view/com/posts/Feed' +import {ScreenHider} from '#/components/moderation/ScreenHider' import {ProfileLists} from '../com/lists/ProfileLists' import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens' -import {ProfileHeader, ProfileHeaderLoading} from '../com/profile/ProfileHeader' import {PagerWithHeader} from 'view/com/pager/PagerWithHeader' import {ErrorScreen} from '../com/util/error/ErrorScreen' -import {EmptyState} from '../com/util/EmptyState' import {FAB} from '../com/util/fab/FAB' import {s, colors} from 'lib/styles' import {useAnalytics} from 'lib/analytics/analytics' import {ComposeIcon2} from 'lib/icons' import {useSetTitle} from 'lib/hooks/useSetTitle' import {combinedDisplayName} from 'lib/strings/display-names' -import { - FeedDescriptor, - resetProfilePostsQueries, -} from '#/state/queries/post-feed' +import {resetProfilePostsQueries} from '#/state/queries/post-feed' import {useResolveDidQuery} from '#/state/queries/resolve-uri' import {useProfileQuery} from '#/state/queries/profile' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useSession, getAgent} from '#/state/session' import {useModerationOpts} from '#/state/queries/preferences' -import {useProfileExtraInfoQuery} from '#/state/queries/profile-extra-info' -import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' +import {useLabelerInfoQuery} from '#/state/queries/labeler' import {useSetDrawerSwipeDisabled, useSetMinimalShellMode} from '#/state/shell' import {cleanError} from '#/lib/strings/errors' -import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn' -import {useQueryClient} from '@tanstack/react-query' import {useComposerControls} from '#/state/shell/composer' import {listenSoftReset} from '#/state/events' -import {truncateAndInvalidate} from '#/state/queries/util' -import {Text} from '#/view/com/util/text/Text' -import {usePalette} from 'lib/hooks/usePalette' -import {isNative} from '#/platform/detection' import {isInvalidHandle} from '#/lib/strings/handles' +import {ProfileFeedSection} from '#/screens/Profile/Sections/Feed' +import {ProfileLabelsSection} from '#/screens/Profile/Sections/Labels' +import {ProfileHeader, ProfileHeaderLoading} from '#/screens/Profile/Header' + interface SectionRef { scrollToTop: () => void } @@ -148,16 +139,24 @@ function ProfileScreenLoaded({ const setMinimalShellMode = useSetMinimalShellMode() const {openComposer} = useComposerControls() const {screen, track} = useAnalytics() + const { + data: labelerInfo, + error: labelerError, + isLoading: isLabelerLoading, + } = useLabelerInfoQuery({ + did: profile.did, + enabled: !!profile.associated?.labeler, + }) const [currentPage, setCurrentPage] = React.useState(0) const {_} = useLingui() const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled() - const extraInfoQuery = useProfileExtraInfoQuery(profile.did) const postsSectionRef = React.useRef<SectionRef>(null) const repliesSectionRef = React.useRef<SectionRef>(null) const mediaSectionRef = React.useRef<SectionRef>(null) const likesSectionRef = React.useRef<SectionRef>(null) const feedsSectionRef = React.useRef<SectionRef>(null) const listsSectionRef = React.useRef<SectionRef>(null) + const labelsSectionRef = React.useRef<SectionRef>(null) useSetTitle(combinedDisplayName(profile)) @@ -171,44 +170,75 @@ function ProfileScreenLoaded({ ) const isMe = profile.did === currentAccount?.did + const hasLabeler = !!profile.associated?.labeler + const showFiltersTab = hasLabeler + const showPostsTab = true const showRepliesTab = hasSession + const showMediaTab = !hasLabeler const showLikesTab = isMe - const showFeedsTab = hasSession && (isMe || extraInfoQuery.data?.hasFeedgens) - const showListsTab = hasSession && (isMe || extraInfoQuery.data?.hasLists) + const showFeedsTab = + hasSession && (isMe || (profile.associated?.feedgens || 0) > 0) + const showListsTab = + hasSession && (isMe || (profile.associated?.lists || 0) > 0) + const sectionTitles = useMemo<string[]>(() => { return [ - _(msg`Posts`), + showFiltersTab ? _(msg`Labels`) : undefined, + showListsTab && hasLabeler ? _(msg`Lists`) : undefined, + showPostsTab ? _(msg`Posts`) : undefined, showRepliesTab ? _(msg`Replies`) : undefined, - _(msg`Media`), + showMediaTab ? _(msg`Media`) : undefined, showLikesTab ? _(msg`Likes`) : undefined, showFeedsTab ? _(msg`Feeds`) : undefined, - showListsTab ? _(msg`Lists`) : undefined, + showListsTab && !hasLabeler ? _(msg`Lists`) : undefined, ].filter(Boolean) as string[] - }, [showRepliesTab, showLikesTab, showFeedsTab, showListsTab, _]) + }, [ + showPostsTab, + showRepliesTab, + showMediaTab, + showLikesTab, + showFeedsTab, + showListsTab, + showFiltersTab, + hasLabeler, + _, + ]) let nextIndex = 0 - const postsIndex = nextIndex++ + let filtersIndex: number | null = null + let postsIndex: number | null = null let repliesIndex: number | null = null + let mediaIndex: number | null = null + let likesIndex: number | null = null + let feedsIndex: number | null = null + let listsIndex: number | null = null + if (showFiltersTab) { + filtersIndex = nextIndex++ + } + if (showPostsTab) { + postsIndex = nextIndex++ + } if (showRepliesTab) { repliesIndex = nextIndex++ } - const mediaIndex = nextIndex++ - let likesIndex: number | null = null + if (showMediaTab) { + mediaIndex = nextIndex++ + } if (showLikesTab) { likesIndex = nextIndex++ } - let feedsIndex: number | null = null if (showFeedsTab) { feedsIndex = nextIndex++ } - let listsIndex: number | null = null if (showListsTab) { listsIndex = nextIndex++ } const scrollSectionToTop = React.useCallback( (index: number) => { - if (index === postsIndex) { + if (index === filtersIndex) { + labelsSectionRef.current?.scrollToTop() + } else if (index === postsIndex) { postsSectionRef.current?.scrollToTop() } else if (index === repliesIndex) { repliesSectionRef.current?.scrollToTop() @@ -222,7 +252,15 @@ function ProfileScreenLoaded({ listsSectionRef.current?.scrollToTop() } }, - [postsIndex, repliesIndex, mediaIndex, likesIndex, feedsIndex, listsIndex], + [ + filtersIndex, + postsIndex, + repliesIndex, + mediaIndex, + likesIndex, + feedsIndex, + listsIndex, + ], ) useFocusEffect( @@ -278,6 +316,7 @@ function ProfileScreenLoaded({ return ( <ProfileHeader profile={profile} + labeler={labelerInfo} descriptionRT={hasDescription ? descriptionRT : null} moderationOpts={moderationOpts} hideBackButton={hideBackButton} @@ -286,6 +325,7 @@ function ProfileScreenLoaded({ ) }, [ profile, + labelerInfo, descriptionRT, hasDescription, moderationOpts, @@ -297,8 +337,8 @@ function ProfileScreenLoaded({ <ScreenHider testID="profileView" style={styles.container} - screenDescription="profile" - moderation={moderation.account}> + screenDescription={_(msg`profile`)} + modui={moderation.ui('profileView')}> <PagerWithHeader testID="profilePager" isHeaderReady={!showPlaceholder} @@ -306,19 +346,45 @@ function ProfileScreenLoaded({ onPageSelected={onPageSelected} onCurrentPageSelected={onCurrentPageSelected} renderHeader={renderHeader}> - {({headerHeight, isFocused, scrollElRef}) => ( - <FeedSection - ref={postsSectionRef} - feed={`author|${profile.did}|posts_and_author_threads`} - headerHeight={headerHeight} - isFocused={isFocused} - scrollElRef={scrollElRef as ListRef} - ignoreFilterFor={profile.did} - /> - )} + {showFiltersTab + ? ({headerHeight, scrollElRef}) => ( + <ProfileLabelsSection + ref={labelsSectionRef} + labelerInfo={labelerInfo} + labelerError={labelerError} + isLabelerLoading={isLabelerLoading} + moderationOpts={moderationOpts} + scrollElRef={scrollElRef as ListRef} + headerHeight={headerHeight} + /> + ) + : null} + {showListsTab && !!profile.associated?.labeler + ? ({headerHeight, isFocused, scrollElRef}) => ( + <ProfileLists + ref={listsSectionRef} + did={profile.did} + scrollElRef={scrollElRef as ListRef} + headerOffset={headerHeight} + enabled={isFocused} + /> + ) + : null} + {showPostsTab + ? ({headerHeight, isFocused, scrollElRef}) => ( + <ProfileFeedSection + ref={postsSectionRef} + feed={`author|${profile.did}|posts_and_author_threads`} + headerHeight={headerHeight} + isFocused={isFocused} + scrollElRef={scrollElRef as ListRef} + ignoreFilterFor={profile.did} + /> + ) + : null} {showRepliesTab ? ({headerHeight, isFocused, scrollElRef}) => ( - <FeedSection + <ProfileFeedSection ref={repliesSectionRef} feed={`author|${profile.did}|posts_with_replies`} headerHeight={headerHeight} @@ -328,19 +394,21 @@ function ProfileScreenLoaded({ /> ) : null} - {({headerHeight, isFocused, scrollElRef}) => ( - <FeedSection - ref={mediaSectionRef} - feed={`author|${profile.did}|posts_with_media`} - headerHeight={headerHeight} - isFocused={isFocused} - scrollElRef={scrollElRef as ListRef} - ignoreFilterFor={profile.did} - /> - )} + {showMediaTab + ? ({headerHeight, isFocused, scrollElRef}) => ( + <ProfileFeedSection + ref={mediaSectionRef} + feed={`author|${profile.did}|posts_with_media`} + headerHeight={headerHeight} + isFocused={isFocused} + scrollElRef={scrollElRef as ListRef} + ignoreFilterFor={profile.did} + /> + ) + : null} {showLikesTab ? ({headerHeight, isFocused, scrollElRef}) => ( - <FeedSection + <ProfileFeedSection ref={likesSectionRef} feed={`likes|${profile.did}`} headerHeight={headerHeight} @@ -361,7 +429,7 @@ function ProfileScreenLoaded({ /> ) : null} - {showListsTab + {showListsTab && !profile.associated?.labeler ? ({headerHeight, isFocused, scrollElRef}) => ( <ProfileLists ref={listsSectionRef} @@ -387,77 +455,6 @@ function ProfileScreenLoaded({ ) } -interface FeedSectionProps { - feed: FeedDescriptor - headerHeight: number - isFocused: boolean - scrollElRef: ListRef - ignoreFilterFor?: string -} -const FeedSection = React.forwardRef<SectionRef, FeedSectionProps>( - function FeedSectionImpl( - {feed, headerHeight, isFocused, scrollElRef, ignoreFilterFor}, - ref, - ) { - const {_} = useLingui() - const queryClient = useQueryClient() - const [hasNew, setHasNew] = React.useState(false) - const [isScrolledDown, setIsScrolledDown] = React.useState(false) - - const onScrollToTop = React.useCallback(() => { - scrollElRef.current?.scrollToOffset({ - animated: isNative, - offset: -headerHeight, - }) - truncateAndInvalidate(queryClient, FEED_RQKEY(feed)) - setHasNew(false) - }, [scrollElRef, headerHeight, queryClient, feed, setHasNew]) - React.useImperativeHandle(ref, () => ({ - scrollToTop: onScrollToTop, - })) - - const renderPostsEmpty = React.useCallback(() => { - return <EmptyState icon="feed" message={_(msg`This feed is empty!`)} /> - }, [_]) - - return ( - <View> - <Feed - testID="postsFeed" - enabled={isFocused} - feed={feed} - scrollElRef={scrollElRef} - onHasNew={setHasNew} - onScrolledDownChange={setIsScrolledDown} - renderEmptyState={renderPostsEmpty} - headerOffset={headerHeight} - renderEndOfFeed={ProfileEndOfFeed} - ignoreFilterFor={ignoreFilterFor} - /> - {(isScrolledDown || hasNew) && ( - <LoadLatestBtn - onPress={onScrollToTop} - label={_(msg`Load new posts`)} - showIndicator={hasNew} - /> - )} - </View> - ) - }, -) - -function ProfileEndOfFeed() { - const pal = usePalette('default') - - return ( - <View style={[pal.border, {paddingTop: 32, borderTopWidth: 1}]}> - <Text style={[pal.textLight, pal.border, {textAlign: 'center'}]}> - <Trans>End of feed</Trans> - </Text> - </View> - ) -} - function useRichText(text: string): [RichTextAPI, boolean] { const [prevText, setPrevText] = React.useState(text) const [rawRT, setRawRT] = React.useState(() => new RichTextAPI({text})) |