Unify label pills (#4676)
* New label pills * Fix type errors, add default case * Remove negative margin, only works in some places * Fix alignment edge case * Add a bit of padding --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
		
							parent
							
								
									c133661768
								
							
						
					
					
						commit
						14c2d75d49
					
				
					 9 changed files with 226 additions and 234 deletions
				
			
		
							
								
								
									
										169
									
								
								src/components/Pills.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/components/Pills.tsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,169 @@
 | 
				
			||||||
 | 
					import React from 'react'
 | 
				
			||||||
 | 
					import {View} from 'react-native'
 | 
				
			||||||
 | 
					import {BSKY_LABELER_DID, ModerationCause} from '@atproto/api'
 | 
				
			||||||
 | 
					import {Trans} from '@lingui/macro'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
 | 
				
			||||||
 | 
					import {UserAvatar} from '#/view/com/util/UserAvatar'
 | 
				
			||||||
 | 
					import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
 | 
				
			||||||
 | 
					import {Button} from '#/components/Button'
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ModerationDetailsDialog,
 | 
				
			||||||
 | 
					  useModerationDetailsDialogControl,
 | 
				
			||||||
 | 
					} from '#/components/moderation/ModerationDetailsDialog'
 | 
				
			||||||
 | 
					import {Text} from '#/components/Typography'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type CommonProps = {
 | 
				
			||||||
 | 
					  size?: 'sm' | 'lg'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Row({
 | 
				
			||||||
 | 
					  children,
 | 
				
			||||||
 | 
					  style,
 | 
				
			||||||
 | 
					  size = 'sm',
 | 
				
			||||||
 | 
					}: {children: React.ReactNode | React.ReactNode[]} & CommonProps &
 | 
				
			||||||
 | 
					  ViewStyleProp) {
 | 
				
			||||||
 | 
					  const styles = React.useMemo(() => {
 | 
				
			||||||
 | 
					    switch (size) {
 | 
				
			||||||
 | 
					      case 'lg':
 | 
				
			||||||
 | 
					        return [{gap: 5}]
 | 
				
			||||||
 | 
					      case 'sm':
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return [{gap: 3}]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [size])
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <View style={[a.flex_row, a.flex_wrap, a.gap_xs, styles, style]}>
 | 
				
			||||||
 | 
					      {children}
 | 
				
			||||||
 | 
					    </View>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type LabelProps = {
 | 
				
			||||||
 | 
					  cause: ModerationCause
 | 
				
			||||||
 | 
					  disableDetailsDialog?: boolean
 | 
				
			||||||
 | 
					  noBg?: boolean
 | 
				
			||||||
 | 
					} & CommonProps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function Label({
 | 
				
			||||||
 | 
					  cause,
 | 
				
			||||||
 | 
					  size = 'sm',
 | 
				
			||||||
 | 
					  disableDetailsDialog,
 | 
				
			||||||
 | 
					  noBg,
 | 
				
			||||||
 | 
					}: LabelProps) {
 | 
				
			||||||
 | 
					  const t = useTheme()
 | 
				
			||||||
 | 
					  const control = useModerationDetailsDialogControl()
 | 
				
			||||||
 | 
					  const desc = useModerationCauseDescription(cause)
 | 
				
			||||||
 | 
					  const isLabeler = Boolean(desc.sourceType && desc.sourceDid)
 | 
				
			||||||
 | 
					  const isBlueskyLabel =
 | 
				
			||||||
 | 
					    desc.sourceType === 'labeler' && desc.sourceDid === BSKY_LABELER_DID
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const {outer, avi, text} = React.useMemo(() => {
 | 
				
			||||||
 | 
					    switch (size) {
 | 
				
			||||||
 | 
					      case 'lg': {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          outer: [
 | 
				
			||||||
 | 
					            t.atoms.bg_contrast_25,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              gap: 5,
 | 
				
			||||||
 | 
					              paddingHorizontal: 5,
 | 
				
			||||||
 | 
					              paddingVertical: 5,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          avi: 16,
 | 
				
			||||||
 | 
					          text: [a.text_sm],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      case 'sm':
 | 
				
			||||||
 | 
					      default: {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					          outer: [
 | 
				
			||||||
 | 
					            !noBg && t.atoms.bg_contrast_25,
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					              gap: 3,
 | 
				
			||||||
 | 
					              paddingHorizontal: 3,
 | 
				
			||||||
 | 
					              paddingVertical: 3,
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					          ],
 | 
				
			||||||
 | 
					          avi: 12,
 | 
				
			||||||
 | 
					          text: [a.text_xs],
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [t, size, noBg])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <>
 | 
				
			||||||
 | 
					      <Button
 | 
				
			||||||
 | 
					        disabled={disableDetailsDialog}
 | 
				
			||||||
 | 
					        label={desc.name}
 | 
				
			||||||
 | 
					        onPress={e => {
 | 
				
			||||||
 | 
					          e.preventDefault()
 | 
				
			||||||
 | 
					          e.stopPropagation()
 | 
				
			||||||
 | 
					          control.open()
 | 
				
			||||||
 | 
					        }}>
 | 
				
			||||||
 | 
					        {({hovered, pressed}) => (
 | 
				
			||||||
 | 
					          <View
 | 
				
			||||||
 | 
					            style={[
 | 
				
			||||||
 | 
					              a.flex_row,
 | 
				
			||||||
 | 
					              a.align_center,
 | 
				
			||||||
 | 
					              a.rounded_full,
 | 
				
			||||||
 | 
					              outer,
 | 
				
			||||||
 | 
					              (hovered || pressed) && t.atoms.bg_contrast_50,
 | 
				
			||||||
 | 
					            ]}>
 | 
				
			||||||
 | 
					            {isBlueskyLabel || !isLabeler ? (
 | 
				
			||||||
 | 
					              <desc.icon
 | 
				
			||||||
 | 
					                width={avi}
 | 
				
			||||||
 | 
					                fill={t.atoms.text_contrast_medium.color}
 | 
				
			||||||
 | 
					              />
 | 
				
			||||||
 | 
					            ) : (
 | 
				
			||||||
 | 
					              <UserAvatar avatar={desc.sourceAvi} size={avi} />
 | 
				
			||||||
 | 
					            )}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Text
 | 
				
			||||||
 | 
					              style={[
 | 
				
			||||||
 | 
					                text,
 | 
				
			||||||
 | 
					                a.font_semibold,
 | 
				
			||||||
 | 
					                a.leading_tight,
 | 
				
			||||||
 | 
					                t.atoms.text_contrast_medium,
 | 
				
			||||||
 | 
					                {paddingRight: 3},
 | 
				
			||||||
 | 
					              ]}>
 | 
				
			||||||
 | 
					              {desc.name}
 | 
				
			||||||
 | 
					            </Text>
 | 
				
			||||||
 | 
					          </View>
 | 
				
			||||||
 | 
					        )}
 | 
				
			||||||
 | 
					      </Button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {!disableDetailsDialog && (
 | 
				
			||||||
 | 
					        <ModerationDetailsDialog control={control} modcause={cause} />
 | 
				
			||||||
 | 
					      )}
 | 
				
			||||||
 | 
					    </>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function FollowsYou({size = 'sm'}: CommonProps) {
 | 
				
			||||||
 | 
					  const t = useTheme()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const variantStyles = React.useMemo(() => {
 | 
				
			||||||
 | 
					    switch (size) {
 | 
				
			||||||
 | 
					      case 'sm':
 | 
				
			||||||
 | 
					      case 'lg':
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        return [
 | 
				
			||||||
 | 
					          {
 | 
				
			||||||
 | 
					            paddingHorizontal: 6,
 | 
				
			||||||
 | 
					            paddingVertical: 3,
 | 
				
			||||||
 | 
					            borderRadius: 4,
 | 
				
			||||||
 | 
					          },
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, [size])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <View style={[variantStyles, a.justify_center, t.atoms.bg_contrast_25]}>
 | 
				
			||||||
 | 
					      <Text style={[a.text_xs, a.leading_tight]}>
 | 
				
			||||||
 | 
					        <Trans>Follows You</Trans>
 | 
				
			||||||
 | 
					      </Text>
 | 
				
			||||||
 | 
					    </View>
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -29,10 +29,10 @@ import {
 | 
				
			||||||
} from '#/components/KnownFollowers'
 | 
					} from '#/components/KnownFollowers'
 | 
				
			||||||
import {InlineLinkText, Link} from '#/components/Link'
 | 
					import {InlineLinkText, Link} from '#/components/Link'
 | 
				
			||||||
import {Loader} from '#/components/Loader'
 | 
					import {Loader} from '#/components/Loader'
 | 
				
			||||||
 | 
					import * as Pills from '#/components/Pills'
 | 
				
			||||||
import {Portal} from '#/components/Portal'
 | 
					import {Portal} from '#/components/Portal'
 | 
				
			||||||
import {RichText} from '#/components/RichText'
 | 
					import {RichText} from '#/components/RichText'
 | 
				
			||||||
import {Text} from '#/components/Typography'
 | 
					import {Text} from '#/components/Typography'
 | 
				
			||||||
import {ProfileLabel} from '../moderation/ProfileHeaderAlerts'
 | 
					 | 
				
			||||||
import {ProfileHoverCardProps} from './types'
 | 
					import {ProfileHoverCardProps} from './types'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const floatingMiddlewares = [
 | 
					const floatingMiddlewares = [
 | 
				
			||||||
| 
						 | 
					@ -476,8 +476,9 @@ function Inner({
 | 
				
			||||||
      {isBlockedUser && (
 | 
					      {isBlockedUser && (
 | 
				
			||||||
        <View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
 | 
					        <View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
 | 
				
			||||||
          {moderation.ui('profileView').alerts.map(cause => (
 | 
					          {moderation.ui('profileView').alerts.map(cause => (
 | 
				
			||||||
            <ProfileLabel
 | 
					            <Pills.Label
 | 
				
			||||||
              key={getModerationCauseKey(cause)}
 | 
					              key={getModerationCauseKey(cause)}
 | 
				
			||||||
 | 
					              size="lg"
 | 
				
			||||||
              cause={cause}
 | 
					              cause={cause}
 | 
				
			||||||
              disableDetailsDialog
 | 
					              disableDetailsDialog
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -214,7 +214,7 @@ function HeaderReady({
 | 
				
			||||||
        ]}>
 | 
					        ]}>
 | 
				
			||||||
        <PostAlerts
 | 
					        <PostAlerts
 | 
				
			||||||
          modui={moderation.ui('contentList')}
 | 
					          modui={moderation.ui('contentList')}
 | 
				
			||||||
          size="large"
 | 
					          size="lg"
 | 
				
			||||||
          style={[a.pt_xs]}
 | 
					          style={[a.pt_xs]}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
      </View>
 | 
					      </View>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,9 +165,7 @@ export function ContentHider({
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const styles = StyleSheet.create({
 | 
					const styles = StyleSheet.create({
 | 
				
			||||||
  outer: {
 | 
					  outer: {},
 | 
				
			||||||
    overflow: 'hidden',
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
  cover: {
 | 
					  cover: {
 | 
				
			||||||
    flexDirection: 'row',
 | 
					    flexDirection: 'row',
 | 
				
			||||||
    alignItems: 'center',
 | 
					    alignItems: 'center',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,17 @@
 | 
				
			||||||
import React from 'react'
 | 
					import React from 'react'
 | 
				
			||||||
import {StyleProp, View, ViewStyle} from 'react-native'
 | 
					import {StyleProp, ViewStyle} from 'react-native'
 | 
				
			||||||
import {BSKY_LABELER_DID, ModerationCause, ModerationUI} from '@atproto/api'
 | 
					import {ModerationUI} from '@atproto/api'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {getModerationCauseKey} from '#/lib/moderation'
 | 
					import {getModerationCauseKey} from '#/lib/moderation'
 | 
				
			||||||
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
 | 
					import * as Pills from '#/components/Pills'
 | 
				
			||||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
 | 
					 | 
				
			||||||
import {atoms as a, useTheme} from '#/alf'
 | 
					 | 
				
			||||||
import {Button} from '#/components/Button'
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  ModerationDetailsDialog,
 | 
					 | 
				
			||||||
  useModerationDetailsDialogControl,
 | 
					 | 
				
			||||||
} from '#/components/moderation/ModerationDetailsDialog'
 | 
					 | 
				
			||||||
import {Text} from '#/components/Typography'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function PostAlerts({
 | 
					export function PostAlerts({
 | 
				
			||||||
  modui,
 | 
					  modui,
 | 
				
			||||||
  size,
 | 
					  size = 'sm',
 | 
				
			||||||
  style,
 | 
					  style,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  modui: ModerationUI
 | 
					  modui: ModerationUI
 | 
				
			||||||
  size?: 'medium' | 'large'
 | 
					  size?: Pills.CommonProps['size']
 | 
				
			||||||
  includeMute?: boolean
 | 
					  includeMute?: boolean
 | 
				
			||||||
  style?: StyleProp<ViewStyle>
 | 
					  style?: StyleProp<ViewStyle>
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
| 
						 | 
					@ -28,90 +20,23 @@ export function PostAlerts({
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <View style={[a.flex_col, a.gap_xs, style]}>
 | 
					    <Pills.Row size={size} style={[size === 'sm' && {marginLeft: -3}, style]}>
 | 
				
			||||||
      <View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
 | 
					      {modui.alerts.map(cause => (
 | 
				
			||||||
        {modui.alerts.map(cause => (
 | 
					        <Pills.Label
 | 
				
			||||||
          <PostLabel
 | 
					          key={getModerationCauseKey(cause)}
 | 
				
			||||||
            key={getModerationCauseKey(cause)}
 | 
					          cause={cause}
 | 
				
			||||||
            cause={cause}
 | 
					          size={size}
 | 
				
			||||||
            size={size}
 | 
					          noBg={size === 'sm'}
 | 
				
			||||||
          />
 | 
					        />
 | 
				
			||||||
        ))}
 | 
					      ))}
 | 
				
			||||||
        {modui.informs.map(cause => (
 | 
					      {modui.informs.map(cause => (
 | 
				
			||||||
          <PostLabel
 | 
					        <Pills.Label
 | 
				
			||||||
            key={getModerationCauseKey(cause)}
 | 
					          key={getModerationCauseKey(cause)}
 | 
				
			||||||
            cause={cause}
 | 
					          cause={cause}
 | 
				
			||||||
            size={size}
 | 
					          size={size}
 | 
				
			||||||
          />
 | 
					          noBg={size === 'sm'}
 | 
				
			||||||
        ))}
 | 
					        />
 | 
				
			||||||
      </View>
 | 
					      ))}
 | 
				
			||||||
    </View>
 | 
					    </Pills.Row>
 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function PostLabel({
 | 
					 | 
				
			||||||
  cause,
 | 
					 | 
				
			||||||
  size,
 | 
					 | 
				
			||||||
}: {
 | 
					 | 
				
			||||||
  cause: ModerationCause
 | 
					 | 
				
			||||||
  size?: 'medium' | 'large'
 | 
					 | 
				
			||||||
}) {
 | 
					 | 
				
			||||||
  const control = useModerationDetailsDialogControl()
 | 
					 | 
				
			||||||
  const desc = useModerationCauseDescription(cause)
 | 
					 | 
				
			||||||
  const t = useTheme()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
      <Button
 | 
					 | 
				
			||||||
        label={desc.name}
 | 
					 | 
				
			||||||
        onPress={e => {
 | 
					 | 
				
			||||||
          e.preventDefault()
 | 
					 | 
				
			||||||
          e.stopPropagation()
 | 
					 | 
				
			||||||
          control.open()
 | 
					 | 
				
			||||||
        }}>
 | 
					 | 
				
			||||||
        {({hovered, pressed}) => (
 | 
					 | 
				
			||||||
          <View
 | 
					 | 
				
			||||||
            style={[
 | 
					 | 
				
			||||||
              a.flex_row,
 | 
					 | 
				
			||||||
              a.align_center,
 | 
					 | 
				
			||||||
              a.gap_xs,
 | 
					 | 
				
			||||||
              a.rounded_sm,
 | 
					 | 
				
			||||||
              hovered || pressed
 | 
					 | 
				
			||||||
                ? size === 'large'
 | 
					 | 
				
			||||||
                  ? t.atoms.bg_contrast_50
 | 
					 | 
				
			||||||
                  : t.atoms.bg_contrast_25
 | 
					 | 
				
			||||||
                : size === 'large'
 | 
					 | 
				
			||||||
                ? t.atoms.bg_contrast_25
 | 
					 | 
				
			||||||
                : undefined,
 | 
					 | 
				
			||||||
              size === 'large'
 | 
					 | 
				
			||||||
                ? {paddingLeft: 4, paddingRight: 6, paddingVertical: 2}
 | 
					 | 
				
			||||||
                : {paddingRight: 4, paddingVertical: 1},
 | 
					 | 
				
			||||||
            ]}>
 | 
					 | 
				
			||||||
            {desc.sourceType === 'labeler' &&
 | 
					 | 
				
			||||||
            desc.sourceDid !== BSKY_LABELER_DID ? (
 | 
					 | 
				
			||||||
              <UserAvatar
 | 
					 | 
				
			||||||
                avatar={desc.sourceAvi}
 | 
					 | 
				
			||||||
                size={size === 'large' ? 16 : 12}
 | 
					 | 
				
			||||||
                type="labeler"
 | 
					 | 
				
			||||||
                shape="circle"
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            ) : (
 | 
					 | 
				
			||||||
              <desc.icon size="sm" fill={t.atoms.text_contrast_medium.color} />
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
            <Text
 | 
					 | 
				
			||||||
              style={[
 | 
					 | 
				
			||||||
                a.text_left,
 | 
					 | 
				
			||||||
                a.leading_snug,
 | 
					 | 
				
			||||||
                size === 'large' ? {fontSize: 13} : a.text_xs,
 | 
					 | 
				
			||||||
                size === 'large' ? t.atoms.text : t.atoms.text_contrast_high,
 | 
					 | 
				
			||||||
              ]}>
 | 
					 | 
				
			||||||
              {desc.name}
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
          </View>
 | 
					 | 
				
			||||||
        )}
 | 
					 | 
				
			||||||
      </Button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      <ModerationDetailsDialog control={control} modcause={cause} />
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,25 +1,12 @@
 | 
				
			||||||
import React from 'react'
 | 
					import React from 'react'
 | 
				
			||||||
import {StyleProp, View, ViewStyle} from 'react-native'
 | 
					import {StyleProp, ViewStyle} from 'react-native'
 | 
				
			||||||
import {
 | 
					import {ModerationDecision} from '@atproto/api'
 | 
				
			||||||
  BSKY_LABELER_DID,
 | 
					 | 
				
			||||||
  ModerationCause,
 | 
					 | 
				
			||||||
  ModerationDecision,
 | 
					 | 
				
			||||||
} from '@atproto/api'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
 | 
					 | 
				
			||||||
import {getModerationCauseKey} from 'lib/moderation'
 | 
					import {getModerationCauseKey} from 'lib/moderation'
 | 
				
			||||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
 | 
					import * as Pills from '#/components/Pills'
 | 
				
			||||||
import {atoms as a, useTheme} from '#/alf'
 | 
					 | 
				
			||||||
import {Button} from '#/components/Button'
 | 
					 | 
				
			||||||
import {
 | 
					 | 
				
			||||||
  ModerationDetailsDialog,
 | 
					 | 
				
			||||||
  useModerationDetailsDialogControl,
 | 
					 | 
				
			||||||
} from '#/components/moderation/ModerationDetailsDialog'
 | 
					 | 
				
			||||||
import {Text} from '#/components/Typography'
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ProfileHeaderAlerts({
 | 
					export function ProfileHeaderAlerts({
 | 
				
			||||||
  moderation,
 | 
					  moderation,
 | 
				
			||||||
  style,
 | 
					 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  moderation: ModerationDecision
 | 
					  moderation: ModerationDecision
 | 
				
			||||||
  style?: StyleProp<ViewStyle>
 | 
					  style?: StyleProp<ViewStyle>
 | 
				
			||||||
| 
						 | 
					@ -30,73 +17,21 @@ export function ProfileHeaderAlerts({
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <View style={[a.flex_col, a.gap_xs, style]}>
 | 
					    <Pills.Row size="lg">
 | 
				
			||||||
      <View style={[a.flex_row, a.flex_wrap, a.gap_xs]}>
 | 
					      {modui.alerts.map(cause => (
 | 
				
			||||||
        {modui.alerts.map(cause => (
 | 
					        <Pills.Label
 | 
				
			||||||
          <ProfileLabel key={getModerationCauseKey(cause)} cause={cause} />
 | 
					          size="lg"
 | 
				
			||||||
        ))}
 | 
					          key={getModerationCauseKey(cause)}
 | 
				
			||||||
        {modui.informs.map(cause => (
 | 
					          cause={cause}
 | 
				
			||||||
          <ProfileLabel key={getModerationCauseKey(cause)} cause={cause} />
 | 
					        />
 | 
				
			||||||
        ))}
 | 
					      ))}
 | 
				
			||||||
      </View>
 | 
					      {modui.informs.map(cause => (
 | 
				
			||||||
    </View>
 | 
					        <Pills.Label
 | 
				
			||||||
  )
 | 
					          size="lg"
 | 
				
			||||||
}
 | 
					          key={getModerationCauseKey(cause)}
 | 
				
			||||||
 | 
					          cause={cause}
 | 
				
			||||||
export function ProfileLabel({
 | 
					        />
 | 
				
			||||||
  cause,
 | 
					      ))}
 | 
				
			||||||
  disableDetailsDialog,
 | 
					    </Pills.Row>
 | 
				
			||||||
}: {
 | 
					 | 
				
			||||||
  cause: ModerationCause
 | 
					 | 
				
			||||||
  disableDetailsDialog?: boolean
 | 
					 | 
				
			||||||
}) {
 | 
					 | 
				
			||||||
  const t = useTheme()
 | 
					 | 
				
			||||||
  const control = useModerationDetailsDialogControl()
 | 
					 | 
				
			||||||
  const desc = useModerationCauseDescription(cause)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
      <Button
 | 
					 | 
				
			||||||
        disabled={disableDetailsDialog}
 | 
					 | 
				
			||||||
        label={desc.name}
 | 
					 | 
				
			||||||
        onPress={() => {
 | 
					 | 
				
			||||||
          control.open()
 | 
					 | 
				
			||||||
        }}>
 | 
					 | 
				
			||||||
        {({hovered, pressed}) => (
 | 
					 | 
				
			||||||
          <View
 | 
					 | 
				
			||||||
            style={[
 | 
					 | 
				
			||||||
              a.flex_row,
 | 
					 | 
				
			||||||
              a.align_center,
 | 
					 | 
				
			||||||
              {paddingLeft: 6, paddingRight: 8, paddingVertical: 4},
 | 
					 | 
				
			||||||
              a.gap_xs,
 | 
					 | 
				
			||||||
              a.rounded_md,
 | 
					 | 
				
			||||||
              hovered || pressed
 | 
					 | 
				
			||||||
                ? t.atoms.bg_contrast_50
 | 
					 | 
				
			||||||
                : t.atoms.bg_contrast_25,
 | 
					 | 
				
			||||||
            ]}>
 | 
					 | 
				
			||||||
            {desc.sourceType === 'labeler' &&
 | 
					 | 
				
			||||||
            desc.sourceDid !== BSKY_LABELER_DID ? (
 | 
					 | 
				
			||||||
              <UserAvatar avatar={desc.sourceAvi} size={16} />
 | 
					 | 
				
			||||||
            ) : (
 | 
					 | 
				
			||||||
              <desc.icon size="sm" fill={t.atoms.text_contrast_medium.color} />
 | 
					 | 
				
			||||||
            )}
 | 
					 | 
				
			||||||
            <Text
 | 
					 | 
				
			||||||
              style={[
 | 
					 | 
				
			||||||
                a.text_left,
 | 
					 | 
				
			||||||
                a.leading_snug,
 | 
					 | 
				
			||||||
                a.text_sm,
 | 
					 | 
				
			||||||
                t.atoms.text_contrast_medium,
 | 
					 | 
				
			||||||
                a.font_semibold,
 | 
					 | 
				
			||||||
              ]}>
 | 
					 | 
				
			||||||
              {desc.name}
 | 
					 | 
				
			||||||
            </Text>
 | 
					 | 
				
			||||||
          </View>
 | 
					 | 
				
			||||||
        )}
 | 
					 | 
				
			||||||
      </Button>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      {!disableDetailsDialog && (
 | 
					 | 
				
			||||||
        <ModerationDetailsDialog control={control} modcause={cause} />
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -315,7 +315,7 @@ function ChatListItemReady({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
              <PostAlerts
 | 
					              <PostAlerts
 | 
				
			||||||
                modui={moderation.ui('contentList')}
 | 
					                modui={moderation.ui('contentList')}
 | 
				
			||||||
                size="large"
 | 
					                size="lg"
 | 
				
			||||||
                style={[a.pt_xs]}
 | 
					                style={[a.pt_xs]}
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
            </View>
 | 
					            </View>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -313,7 +313,7 @@ let PostThreadItemLoaded = ({
 | 
				
			||||||
              childContainerStyle={styles.contentHiderChild}>
 | 
					              childContainerStyle={styles.contentHiderChild}>
 | 
				
			||||||
              <PostAlerts
 | 
					              <PostAlerts
 | 
				
			||||||
                modui={moderation.ui('contentView')}
 | 
					                modui={moderation.ui('contentView')}
 | 
				
			||||||
                size="large"
 | 
					                size="lg"
 | 
				
			||||||
                includeMute
 | 
					                includeMute
 | 
				
			||||||
                style={[a.pt_2xs, a.pb_sm]}
 | 
					                style={[a.pt_2xs, a.pb_sm]}
 | 
				
			||||||
              />
 | 
					              />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,13 +3,11 @@ import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  AppBskyActorDefs,
 | 
					  AppBskyActorDefs,
 | 
				
			||||||
  moderateProfile,
 | 
					  moderateProfile,
 | 
				
			||||||
  ModerationCause,
 | 
					 | 
				
			||||||
  ModerationDecision,
 | 
					  ModerationDecision,
 | 
				
			||||||
} from '@atproto/api'
 | 
					} from '@atproto/api'
 | 
				
			||||||
import {Trans} from '@lingui/macro'
 | 
					import {Trans} from '@lingui/macro'
 | 
				
			||||||
import {useQueryClient} from '@tanstack/react-query'
 | 
					import {useQueryClient} from '@tanstack/react-query'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription'
 | 
					 | 
				
			||||||
import {useProfileShadow} from '#/state/cache/profile-shadow'
 | 
					import {useProfileShadow} from '#/state/cache/profile-shadow'
 | 
				
			||||||
import {Shadow} from '#/state/cache/types'
 | 
					import {Shadow} from '#/state/cache/types'
 | 
				
			||||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
 | 
					import {useModerationOpts} from '#/state/preferences/moderation-opts'
 | 
				
			||||||
| 
						 | 
					@ -26,6 +24,8 @@ import {Text} from '../util/text/Text'
 | 
				
			||||||
import {PreviewableUserAvatar} from '../util/UserAvatar'
 | 
					import {PreviewableUserAvatar} from '../util/UserAvatar'
 | 
				
			||||||
import {FollowButton} from './FollowButton'
 | 
					import {FollowButton} from './FollowButton'
 | 
				
			||||||
import hairlineWidth = StyleSheet.hairlineWidth
 | 
					import hairlineWidth = StyleSheet.hairlineWidth
 | 
				
			||||||
 | 
					import {atoms as a} from '#/alf'
 | 
				
			||||||
 | 
					import * as Pills from '#/components/Pills'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function ProfileCard({
 | 
					export function ProfileCard({
 | 
				
			||||||
  testID,
 | 
					  testID,
 | 
				
			||||||
| 
						 | 
					@ -137,58 +137,21 @@ export function ProfileCardPills({
 | 
				
			||||||
  followedBy: boolean
 | 
					  followedBy: boolean
 | 
				
			||||||
  moderation: ModerationDecision
 | 
					  moderation: ModerationDecision
 | 
				
			||||||
}) {
 | 
					}) {
 | 
				
			||||||
  const pal = usePalette('default')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  const modui = moderation.ui('profileList')
 | 
					  const modui = moderation.ui('profileList')
 | 
				
			||||||
  if (!followedBy && !modui.inform && !modui.alert) {
 | 
					  if (!followedBy && !modui.inform && !modui.alert) {
 | 
				
			||||||
    return null
 | 
					    return null
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <View style={styles.pills}>
 | 
					    <Pills.Row style={[a.pt_xs]}>
 | 
				
			||||||
      {followedBy && (
 | 
					      {followedBy && <Pills.FollowsYou />}
 | 
				
			||||||
        <View style={[s.mt5, pal.btn, styles.pill]}>
 | 
					 | 
				
			||||||
          <Text type="xs" style={pal.text}>
 | 
					 | 
				
			||||||
            <Trans>Follows You</Trans>
 | 
					 | 
				
			||||||
          </Text>
 | 
					 | 
				
			||||||
        </View>
 | 
					 | 
				
			||||||
      )}
 | 
					 | 
				
			||||||
      {modui.alerts.map(alert => (
 | 
					      {modui.alerts.map(alert => (
 | 
				
			||||||
        <ProfileCardPillModerationCause
 | 
					        <Pills.Label key={getModerationCauseKey(alert)} cause={alert} />
 | 
				
			||||||
          key={getModerationCauseKey(alert)}
 | 
					 | 
				
			||||||
          cause={alert}
 | 
					 | 
				
			||||||
          severity="alert"
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      ))}
 | 
					      ))}
 | 
				
			||||||
      {modui.informs.map(inform => (
 | 
					      {modui.informs.map(inform => (
 | 
				
			||||||
        <ProfileCardPillModerationCause
 | 
					        <Pills.Label key={getModerationCauseKey(inform)} cause={inform} />
 | 
				
			||||||
          key={getModerationCauseKey(inform)}
 | 
					 | 
				
			||||||
          cause={inform}
 | 
					 | 
				
			||||||
          severity="inform"
 | 
					 | 
				
			||||||
        />
 | 
					 | 
				
			||||||
      ))}
 | 
					      ))}
 | 
				
			||||||
    </View>
 | 
					    </Pills.Row>
 | 
				
			||||||
  )
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function ProfileCardPillModerationCause({
 | 
					 | 
				
			||||||
  cause,
 | 
					 | 
				
			||||||
  severity,
 | 
					 | 
				
			||||||
}: {
 | 
					 | 
				
			||||||
  cause: ModerationCause
 | 
					 | 
				
			||||||
  severity: 'alert' | 'inform'
 | 
					 | 
				
			||||||
}) {
 | 
					 | 
				
			||||||
  const pal = usePalette('default')
 | 
					 | 
				
			||||||
  const {name} = useModerationCauseDescription(cause)
 | 
					 | 
				
			||||||
  return (
 | 
					 | 
				
			||||||
    <View
 | 
					 | 
				
			||||||
      style={[s.mt5, pal.btn, styles.pill]}
 | 
					 | 
				
			||||||
      key={getModerationCauseKey(cause)}>
 | 
					 | 
				
			||||||
      <Text type="xs" style={pal.text}>
 | 
					 | 
				
			||||||
        {severity === 'alert' ? '⚠ ' : ''}
 | 
					 | 
				
			||||||
        {name}
 | 
					 | 
				
			||||||
      </Text>
 | 
					 | 
				
			||||||
    </View>
 | 
					 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -322,6 +285,7 @@ const styles = StyleSheet.create({
 | 
				
			||||||
    paddingBottom: 10,
 | 
					    paddingBottom: 10,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  pills: {
 | 
					  pills: {
 | 
				
			||||||
 | 
					    alignItems: 'flex-start',
 | 
				
			||||||
    flexDirection: 'row',
 | 
					    flexDirection: 'row',
 | 
				
			||||||
    flexWrap: 'wrap',
 | 
					    flexWrap: 'wrap',
 | 
				
			||||||
    columnGap: 6,
 | 
					    columnGap: 6,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue