Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Enable access for group clip clops with no messages (#10276)

authored by

DS Boyce and committed by
GitHub
9e9ff706 226a321a

+157 -110
+141 -92
src/components/dms/MessagesListHeader.tsx
··· 13 13 import {type NavigationProp} from '#/lib/routes/types' 14 14 import {logger} from '#/logger' 15 15 import {type Shadow} from '#/state/cache/profile-shadow' 16 - import {isConvoActive, useConvo} from '#/state/messages/convo' 16 + import { 17 + type ActiveConvoStates, 18 + isConvoActive, 19 + useConvo, 20 + } from '#/state/messages/convo' 17 21 import {type ConvoItem} from '#/state/messages/convo/types' 18 22 import {useSession} from '#/state/session' 19 23 import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' ··· 36 40 moderation, 37 41 }: { 38 42 profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed> 39 - moderation?: ModerationDecision 43 + moderation?: ModerationDecision | null 40 44 }) { 41 45 const t = useTheme() 42 46 47 + const convoState = useConvo() 48 + const isGroupChat = convoState?.isGroup?.() 49 + 43 50 const blockInfo = useMemo(() => { 44 51 if (!moderation) return 45 52 const modui = moderation.ui('profileView') ··· 58 65 <View style={[{minHeight: PFP_SIZE}, a.justify_center]}> 59 66 <Layout.Header.BackButton /> 60 67 </View> 61 - {profile && moderation && blockInfo ? ( 62 - <HeaderReady 63 - profile={profile} 64 - moderation={moderation} 65 - blockInfo={blockInfo} 66 - /> 68 + {isConvoActive(convoState) ? ( 69 + moderation && blockInfo && profile && !isGroupChat ? ( 70 + <ProfileHeaderReady 71 + convoState={convoState} 72 + profile={profile} 73 + moderation={moderation} 74 + blockInfo={blockInfo} 75 + /> 76 + ) : ( 77 + <GroupHeaderReady 78 + convoState={convoState} 79 + profile={profile} 80 + moderation={moderation} 81 + /> 82 + ) 67 83 ) : ( 68 84 <> 69 85 <View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1]}> ··· 94 110 ) 95 111 } 96 112 97 - function HeaderReady({ 113 + function ProfileHeaderReady({ 114 + convoState, 98 115 profile, 99 116 moderation, 100 117 blockInfo, 101 118 }: { 119 + convoState: ActiveConvoStates 102 120 profile: Shadow<AppBskyActorDefs.ProfileViewDetailed> 103 121 moderation: ModerationDecision 104 122 blockInfo: { ··· 107 125 } 108 126 }) { 109 127 const {t: l} = useLingui() 110 - const t = useTheme() 111 - const convoState = useConvo() 112 128 const {currentAccount} = useSession() 113 129 114 - const navigation = useNavigation<NavigationProp>() 115 - 116 - const groupInfo = convoState.getGroupInfo?.() 117 - const isGroupChat = groupInfo != null 118 - 119 130 const isDeletedAccount = profile?.handle === 'missing.invalid' 120 - const displayName = isGroupChat 121 - ? (groupInfo.name ?? l`${profile.handle}'s group chat`) 122 - : isDeletedAccount 123 - ? l`Deleted Account` 124 - : createSanitizedDisplayName(profile, true, moderation.ui('displayName')) 131 + const displayName = isDeletedAccount 132 + ? l`Deleted Account` 133 + : createSanitizedDisplayName(profile, true, moderation.ui('displayName')) 125 134 126 135 const latestMessageFromOther = convoState.items.findLast( 127 136 (item: ConvoItem) => ··· 134 143 ? latestMessageFromOther.message 135 144 : undefined 136 145 146 + return ( 147 + <Wrapper 148 + heading={ 149 + <Link 150 + label={l`View ${displayName}’s profile`} 151 + style={[a.flex_row, a.gap_md, a.flex_1, a.pr_md]} 152 + to={makeProfileLink(profile)}> 153 + <PreviewableUserAvatar 154 + size={PFP_SIZE} 155 + profile={profile} 156 + moderation={moderation.ui('avatar')} 157 + disableHoverCard={moderation.blocked} 158 + /> 159 + <ProfileBadges profile={profile} size="md" style={[a.pl_xs]} /> 160 + </Link> 161 + } 162 + muted={convoState.convo?.muted} 163 + settings={ 164 + isConvoActive(convoState) ? ( 165 + <ConvoMenu 166 + convo={convoState.convo} 167 + profile={profile} 168 + currentScreen="conversation" 169 + blockInfo={blockInfo} 170 + latestReportableMessage={latestReportableMessage} 171 + /> 172 + ) : null 173 + } 174 + /> 175 + ) 176 + } 177 + 178 + function GroupHeaderReady({ 179 + convoState, 180 + profile, 181 + moderation, 182 + }: { 183 + convoState: ActiveConvoStates 184 + profile?: Shadow<AppBskyActorDefs.ProfileViewDetailed> 185 + moderation?: ModerationDecision | null 186 + }) { 187 + const {t: l} = useLingui() 188 + 189 + const navigation = useNavigation<NavigationProp>() 190 + 191 + const groupInfo = convoState.getGroupInfo?.() 192 + 193 + const isDeletedAccount = profile?.handle === 'missing.invalid' 194 + const displayName = isDeletedAccount 195 + ? l`Deleted Account` 196 + : profile 197 + ? createSanitizedDisplayName(profile, true, moderation?.ui('displayName')) 198 + : undefined 199 + const groupName = 200 + groupInfo?.name ?? 201 + (displayName ? l`${displayName}’s group chat` : l`Group chat`) 202 + 137 203 const handleNavigateToSettings = () => { 138 204 const convoId = convoState.convo?.id 139 205 if (convoId) { ··· 146 212 } 147 213 148 214 return ( 215 + <Wrapper 216 + heading={ 217 + <> 218 + <AvatarBubbles size="small" profiles={convoState.recipients ?? []} /> 219 + <Text style={[a.text_md, a.font_semi_bold]} numberOfLines={1}> 220 + {groupName} 221 + </Text> 222 + </> 223 + } 224 + muted={convoState.convo?.muted} 225 + settings={ 226 + isConvoActive(convoState) ? ( 227 + <Button 228 + label={l`Open group chat settings`} 229 + size="small" 230 + color="secondary" 231 + shape="round" 232 + variant="ghost" 233 + style={[a.bg_transparent]} 234 + onPress={handleNavigateToSettings}> 235 + <ButtonIcon icon={DotsHorizontalIcon} size="md" /> 236 + </Button> 237 + ) : null 238 + } 239 + /> 240 + ) 241 + } 242 + 243 + function Wrapper({ 244 + heading, 245 + muted, 246 + settings, 247 + }: { 248 + heading: React.ReactNode 249 + muted: boolean 250 + settings: React.ReactNode 251 + }) { 252 + return ( 149 253 <View style={[a.flex_1]}> 150 254 <View style={[a.w_full, a.flex_row, a.align_center, a.justify_between]}> 151 - {isGroupChat ? ( 152 - <View 153 - style={[a.flex_row, a.align_center, a.gap_md, a.flex_1, a.pr_md]}> 154 - <AvatarBubbles 155 - size="small" 156 - profiles={convoState.recipients ?? []} 157 - /> 158 - <Text style={[a.text_md, a.font_semi_bold]} numberOfLines={1}> 159 - {displayName} 160 - </Text> 161 - </View> 162 - ) : ( 163 - <Link 164 - label={l`View ${displayName}'s profile`} 165 - style={[a.flex_row, a.gap_md, a.flex_1, a.pr_md]} 166 - to={makeProfileLink(profile)}> 167 - <PreviewableUserAvatar 168 - size={PFP_SIZE} 169 - profile={profile} 170 - moderation={moderation.ui('avatar')} 171 - disableHoverCard={moderation.blocked} 172 - /> 173 - <View style={[a.flex_1]}> 174 - <View style={[a.flex_row, a.align_center]}> 175 - <Text 176 - emoji 177 - style={[a.text_md, a.font_semi_bold, a.self_start]} 178 - numberOfLines={1}> 179 - {displayName} 180 - </Text> 181 - <ProfileBadges profile={profile} size="md" style={[a.pl_xs]} /> 182 - {convoState.convo?.muted && ( 183 - <> 184 - <Text style={[a.text_md, t.atoms.text_contrast_medium]}> 185 - {' '} 186 - &middot;{' '} 187 - </Text> 188 - <BellOffIcon 189 - size="sm" 190 - style={t.atoms.text_contrast_medium} 191 - /> 192 - </> 193 - )} 194 - </View> 195 - </View> 196 - </Link> 197 - )} 255 + <View style={[a.flex_row, a.align_center, a.gap_md, a.flex_1, a.pr_md]}> 256 + {heading} 257 + <MuteStatus muted={muted} /> 258 + </View> 198 259 199 260 <View style={[{minHeight: PFP_SIZE}, a.justify_center]}> 200 - <Layout.Header.Slot> 201 - {isConvoActive(convoState) ? ( 202 - isGroupChat ? ( 203 - <Button 204 - label={l`Open group chat settings`} 205 - size="small" 206 - color="secondary" 207 - shape="round" 208 - variant="ghost" 209 - style={[a.bg_transparent]} 210 - onPress={handleNavigateToSettings}> 211 - <ButtonIcon icon={DotsHorizontalIcon} size="md" /> 212 - </Button> 213 - ) : ( 214 - <ConvoMenu 215 - convo={convoState.convo} 216 - profile={profile} 217 - currentScreen="conversation" 218 - blockInfo={blockInfo} 219 - latestReportableMessage={latestReportableMessage} 220 - /> 221 - ) 222 - ) : null} 223 - </Layout.Header.Slot> 261 + <Layout.Header.Slot>{settings}</Layout.Header.Slot> 224 262 </View> 225 263 </View> 226 264 </View> 227 265 ) 228 266 } 267 + 268 + function MuteStatus({muted}: {muted: boolean}) { 269 + const t = useTheme() 270 + 271 + return muted ? ( 272 + <> 273 + <Text style={[a.text_md, t.atoms.text_contrast_medium]}> &middot; </Text> 274 + <BellOffIcon size="sm" style={t.atoms.text_contrast_medium} /> 275 + </> 276 + ) : undefined 277 + }
+16 -18
src/screens/Messages/Conversation.tsx
··· 173 173 </View> 174 174 )} 175 175 <View style={[a.flex_1]}> 176 - {moderation && recipient ? ( 177 - <InnerReady 178 - moderation={moderation} 179 - recipient={recipient} 180 - hasScrolled={hasScrolled} 181 - setHasScrolled={setHasScrolled} 182 - /> 183 - ) : ( 184 - <View style={[a.align_center, a.gap_sm, a.flex_1]} /> 185 - )} 176 + <InnerReady 177 + moderation={moderation} 178 + recipient={recipient} 179 + hasScrolled={hasScrolled} 180 + setHasScrolled={setHasScrolled} 181 + /> 186 182 {!readyToShow && ( 187 183 <View 188 184 style={[ ··· 210 206 hasScrolled, 211 207 setHasScrolled, 212 208 }: { 213 - moderation: ModerationDecision 214 - recipient: Shadow<AppBskyActorDefs.ProfileViewDetailed> 209 + moderation: ModerationDecision | null 210 + recipient: Shadow<AppBskyActorDefs.ProfileViewDetailed> | undefined 215 211 hasScrolled: boolean 216 212 setHasScrolled: React.Dispatch<React.SetStateAction<boolean>> 217 213 }) { ··· 289 285 hasAcceptOverride={!!params.accept} 290 286 transparentHeaderHeight={IS_LIQUID_GLASS ? headerHeight : 0} 291 287 footer={ 292 - <MessagesListBlockedFooter 293 - recipient={recipient} 294 - convoId={convoState.convo.id} 295 - hasMessages={convoState.items.length > 0} 296 - moderation={moderation} 297 - /> 288 + moderation && recipient ? ( 289 + <MessagesListBlockedFooter 290 + recipient={recipient} 291 + convoId={convoState.convo.id} 292 + hasMessages={convoState.items.length > 0} 293 + moderation={moderation} 294 + /> 295 + ) : null 298 296 } 299 297 /> 300 298 )}