Bluesky app fork with some witchin' additions 馃挮
at feat/tealfm 154 lines 3.8 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {BSKY_LABELER_DID, type ModerationCause} from '@atproto/api' 4 5import {useModerationCauseDescription} from '#/lib/moderation/useModerationCauseDescription' 6import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 7import {UserAvatar} from '#/view/com/util/UserAvatar' 8import {atoms as a, useTheme, type ViewStyleProp} from '#/alf' 9import {Button} from '#/components/Button' 10import { 11 ModerationDetailsDialog, 12 useModerationDetailsDialogControl, 13} from '#/components/moderation/ModerationDetailsDialog' 14import {Text} from '#/components/Typography' 15 16export type AppModerationCause = 17 | ModerationCause 18 | { 19 type: 'reply-hidden' 20 source: {type: 'user'; did: string} 21 priority: 6 22 downgraded?: boolean 23 } 24 25export type CommonProps = { 26 size?: 'sm' | 'lg' 27} 28 29export function Row({ 30 children, 31 style, 32 size = 'sm', 33}: {children: React.ReactNode | React.ReactNode[]} & CommonProps & 34 ViewStyleProp) { 35 const styles = React.useMemo(() => { 36 switch (size) { 37 case 'lg': 38 return [{gap: 5}] 39 case 'sm': 40 default: 41 return [{gap: 3}] 42 } 43 }, [size]) 44 return ( 45 <View style={[a.flex_row, a.flex_wrap, a.gap_xs, styles, style]}> 46 {children} 47 </View> 48 ) 49} 50 51export type LabelProps = { 52 cause: AppModerationCause 53 disableDetailsDialog?: boolean 54 noBg?: boolean 55} & CommonProps 56 57export function Label({ 58 cause, 59 size = 'sm', 60 disableDetailsDialog, 61 noBg, 62}: LabelProps) { 63 const t = useTheme() 64 const control = useModerationDetailsDialogControl() 65 const desc = useModerationCauseDescription(cause) 66 const isLabeler = Boolean(desc.sourceType && desc.sourceDid) 67 const isBlueskyLabel = 68 desc.sourceType === 'labeler' && desc.sourceDid === BSKY_LABELER_DID 69 70 const enableSquareButtons = useEnableSquareButtons() 71 72 const {outer, avi, text} = React.useMemo(() => { 73 switch (size) { 74 case 'lg': { 75 return { 76 outer: [ 77 t.atoms.bg_contrast_25, 78 { 79 gap: 5, 80 paddingHorizontal: 5, 81 paddingVertical: 5, 82 }, 83 ], 84 avi: 16, 85 text: [a.text_sm], 86 } 87 } 88 case 'sm': 89 default: { 90 return { 91 outer: [ 92 !noBg && t.atoms.bg_contrast_25, 93 { 94 gap: 3, 95 paddingHorizontal: 3, 96 paddingVertical: 3, 97 }, 98 ], 99 avi: 12, 100 text: [a.text_xs], 101 } 102 } 103 } 104 }, [t, size, noBg]) 105 106 return ( 107 <> 108 <Button 109 disabled={disableDetailsDialog} 110 label={desc.name} 111 onPress={e => { 112 e.preventDefault() 113 e.stopPropagation() 114 control.open() 115 }}> 116 {({hovered, pressed}) => ( 117 <View 118 style={[ 119 a.flex_row, 120 a.align_center, 121 enableSquareButtons ? a.rounded_sm : a.rounded_full, 122 outer, 123 (hovered || pressed) && t.atoms.bg_contrast_50, 124 ]}> 125 {isBlueskyLabel || !isLabeler ? ( 126 <desc.icon 127 width={avi} 128 fill={t.atoms.text_contrast_medium.color} 129 /> 130 ) : ( 131 <UserAvatar avatar={desc.sourceAvi} type="user" size={avi} /> 132 )} 133 134 <Text 135 emoji 136 style={[ 137 text, 138 a.font_semi_bold, 139 a.leading_tight, 140 t.atoms.text_contrast_medium, 141 {paddingRight: 3}, 142 ]}> 143 {desc.name} 144 </Text> 145 </View> 146 )} 147 </Button> 148 149 {!disableDetailsDialog && ( 150 <ModerationDetailsDialog control={control} modcause={cause} /> 151 )} 152 </> 153 ) 154}