mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

[APP-657] Add share list functionality (#863)

* replace delete list button text with icon

* fix mute list styling on desktop

* add share button to nav bar on a list

* fix styling when on profile

* bug: add key to ImageHorzList

* clean up code & refactor

* fix styling for ListItems

* create a reusable ListActions component for actions on a list

* remove dead styles

* add keys to ListActions

* add helpers to set list embed

* render list embeds

* fix list sharing on web

* make style prop optional in ListCard

* update `@atproto/api` to `0.3.13`

authored by

Ansh and committed by
GitHub
b9abd444 1666a747

+320 -145
+1 -1
package.json
··· 23 23 "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all" 24 24 }, 25 25 "dependencies": { 26 - "@atproto/api": "0.3.8", 26 + "@atproto/api": "^0.3.13", 27 27 "@bam.tech/react-native-image-resizer": "^3.0.4", 28 28 "@braintree/sanitize-url": "^6.0.2", 29 29 "@expo/html-elements": "^0.4.2",
+14
src/lib/strings/url-helpers.ts
··· 94 94 return false 95 95 } 96 96 97 + export function isBskyListUrl(url: string): boolean { 98 + if (isBskyAppUrl(url)) { 99 + try { 100 + const urlp = new URL(url) 101 + return /profile\/(?<name>[^/]+)\/lists\/(?<rkey>[^/]+)/i.test( 102 + urlp.pathname, 103 + ) 104 + } catch { 105 + console.error('Unexpected error in isBskyListUrl()', url) 106 + } 107 + } 108 + return false 109 + } 110 + 97 111 export function convertBskyAppUrlIfNeeded(url: string): string { 98 112 if (isBskyAppUrl(url)) { 99 113 try {
+4
src/state/models/content/list.ts
··· 97 97 return this.list?.creator.did === this.rootStore.me.did 98 98 } 99 99 100 + get isSubscribed() { 101 + return this.list?.viewer?.muted 102 + } 103 + 100 104 // public api 101 105 // = 102 106
+28 -2
src/view/com/composer/useExternalLinkFetch.ts
··· 3 3 import {ImageModel} from 'state/models/media/image' 4 4 import * as apilib from 'lib/api/index' 5 5 import {getLinkMeta} from 'lib/link-meta/link-meta' 6 - import {getPostAsQuote, getFeedAsEmbed} from 'lib/link-meta/bsky' 6 + import { 7 + getPostAsQuote, 8 + getFeedAsEmbed, 9 + getListAsEmbed, 10 + } from 'lib/link-meta/bsky' 7 11 import {downloadAndResize} from 'lib/media/manip' 8 - import {isBskyPostUrl, isBskyCustomFeedUrl} from 'lib/strings/url-helpers' 12 + import { 13 + isBskyPostUrl, 14 + isBskyCustomFeedUrl, 15 + isBskyListUrl, 16 + } from 'lib/strings/url-helpers' 9 17 import {ComposerOpts} from 'state/models/ui/shell' 10 18 import {POST_IMG_MAX} from 'lib/constants' 11 19 ··· 57 65 }, 58 66 err => { 59 67 store.log.error('Failed to fetch feed for embedding', {err}) 68 + setExtLink(undefined) 69 + }, 70 + ) 71 + } else if (isBskyListUrl(extLink.uri)) { 72 + getListAsEmbed(store, extLink.uri).then( 73 + ({embed, meta}) => { 74 + if (aborted) { 75 + return 76 + } 77 + setExtLink({ 78 + uri: extLink.uri, 79 + isLoading: false, 80 + meta, 81 + embed, 82 + }) 83 + }, 84 + err => { 85 + store.log.error('Failed to fetch list for embedding', {err}) 60 86 setExtLink(undefined) 61 87 }, 62 88 )
+83
src/view/com/lists/ListActions.tsx
··· 1 + import React from 'react' 2 + import {StyleSheet, View} from 'react-native' 3 + import {Button} from '../util/forms/Button' 4 + import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 5 + import {usePalette} from 'lib/hooks/usePalette' 6 + 7 + export const ListActions = ({ 8 + muted, 9 + onToggleSubscribed, 10 + onPressEditList, 11 + isOwner, 12 + onPressDeleteList, 13 + onPressShareList, 14 + reversed = false, // Default value of reversed is false 15 + }: { 16 + isOwner: boolean 17 + muted?: boolean 18 + onToggleSubscribed?: () => void 19 + onPressEditList?: () => void 20 + onPressDeleteList?: () => void 21 + onPressShareList?: () => void 22 + reversed?: boolean // New optional prop 23 + }) => { 24 + const pal = usePalette('default') 25 + 26 + let buttons = [ 27 + <Button 28 + key="subscribeButton" 29 + type={muted ? 'inverted' : 'primary'} 30 + label={muted ? 'Unsubscribe' : 'Subscribe & Mute'} 31 + accessibilityLabel={muted ? 'Unsubscribe' : 'Subscribe and mute'} 32 + accessibilityHint="" 33 + onPress={onToggleSubscribed} 34 + />, 35 + isOwner && ( 36 + <Button 37 + key="editListButton" 38 + type="default" 39 + label="Edit List" 40 + accessibilityLabel="Edit list" 41 + accessibilityHint="" 42 + onPress={onPressEditList} 43 + /> 44 + ), 45 + isOwner && ( 46 + <Button 47 + key="deleteListButton" 48 + type="default" 49 + testID="deleteListBtn" 50 + accessibilityLabel="Delete list" 51 + accessibilityHint="" 52 + onPress={onPressDeleteList}> 53 + <FontAwesomeIcon icon={['far', 'trash-can']} style={[pal.text]} /> 54 + </Button> 55 + ), 56 + <Button 57 + key="shareListButton" 58 + type="default" 59 + testID="shareListBtn" 60 + accessibilityLabel="Share list" 61 + accessibilityHint="" 62 + onPress={onPressShareList}> 63 + <FontAwesomeIcon icon={'share'} style={[pal.text]} /> 64 + </Button>, 65 + ] 66 + 67 + // If reversed is true, reverse the array to reverse the order of the buttons 68 + if (reversed) { 69 + buttons = buttons.filter(Boolean).reverse() // filterting out any falsey values and reversing the array 70 + } else { 71 + buttons = buttons.filter(Boolean) // filterting out any falsey values 72 + } 73 + 74 + return <View style={styles.headerBtns}>{buttons}</View> 75 + } 76 + 77 + const styles = StyleSheet.create({ 78 + headerBtns: { 79 + flexDirection: 'row', 80 + gap: 8, 81 + marginTop: 12, 82 + }, 83 + })
+4 -1
src/view/com/lists/ListCard.tsx
··· 1 1 import React from 'react' 2 - import {StyleSheet, View} from 'react-native' 2 + import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3 3 import {AtUri, AppBskyGraphDefs, RichText} from '@atproto/api' 4 4 import {Link} from '../util/Link' 5 5 import {Text} from '../util/text/Text' ··· 16 16 noBg, 17 17 noBorder, 18 18 renderButton, 19 + style, 19 20 }: { 20 21 testID?: string 21 22 list: AppBskyGraphDefs.ListView 22 23 noBg?: boolean 23 24 noBorder?: boolean 24 25 renderButton?: () => JSX.Element 26 + style?: StyleProp<ViewStyle> 25 27 }) => { 26 28 const pal = usePalette('default') 27 29 const store = useStores() ··· 53 55 pal.border, 54 56 noBorder && styles.outerNoBorder, 55 57 !noBg && pal.view, 58 + style, 56 59 ]} 57 60 href={`/profile/${list.creator.did}/lists/${rkey}`} 58 61 title={list.name}
+29 -49
src/view/com/lists/ListItems.tsx
··· 6 6 StyleSheet, 7 7 View, 8 8 ViewStyle, 9 + FlatList, 9 10 } from 'react-native' 10 11 import {AppBskyActorDefs, AppBskyGraphDefs, RichText} from '@atproto/api' 11 12 import {observer} from 'mobx-react-lite' 12 - import {FlatList} from '../util/Views' 13 13 import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' 14 14 import {ErrorMessage} from '../util/error/ErrorMessage' 15 15 import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' ··· 25 25 import {useStores} from 'state/index' 26 26 import {s} from 'lib/styles' 27 27 import {isDesktopWeb} from 'platform/detection' 28 + import {ListActions} from './ListActions' 28 29 29 30 const LOADING_ITEM = {_reactKey: '__loading__'} 30 31 const HEADER_ITEM = {_reactKey: '__header__'} ··· 41 42 onToggleSubscribed, 42 43 onPressEditList, 43 44 onPressDeleteList, 45 + onPressShareList, 44 46 renderEmptyState, 45 47 testID, 46 48 headerOffset = 0, ··· 49 51 style?: StyleProp<ViewStyle> 50 52 scrollElRef?: MutableRefObject<FlatList<any> | null> 51 53 onPressTryAgain?: () => void 52 - onToggleSubscribed?: () => void 53 - onPressEditList?: () => void 54 - onPressDeleteList?: () => void 54 + onToggleSubscribed: () => void 55 + onPressEditList: () => void 56 + onPressDeleteList: () => void 57 + onPressShareList: () => void 55 58 renderEmptyState?: () => JSX.Element 56 59 testID?: string 57 60 headerOffset?: number ··· 163 166 onToggleSubscribed={onToggleSubscribed} 164 167 onPressEditList={onPressEditList} 165 168 onPressDeleteList={onPressDeleteList} 169 + onPressShareList={onPressShareList} 166 170 /> 167 171 ) : null 168 172 } else if (item === ERROR_ITEM) { ··· 193 197 ) 194 198 }, 195 199 [ 196 - list, 197 - onPressTryAgain, 198 - onPressRetryLoadMore, 199 200 renderMemberButton, 201 + renderEmptyState, 202 + list.list, 203 + list.isOwner, 204 + list.error, 205 + onToggleSubscribed, 200 206 onPressEditList, 201 207 onPressDeleteList, 202 - onToggleSubscribed, 203 - renderEmptyState, 208 + onPressShareList, 209 + onPressTryAgain, 210 + onPressRetryLoadMore, 204 211 ], 205 212 ) 206 213 ··· 257 264 onToggleSubscribed, 258 265 onPressEditList, 259 266 onPressDeleteList, 267 + onPressShareList, 260 268 }: { 261 269 list: AppBskyGraphDefs.ListView 262 270 isOwner: boolean 263 - onToggleSubscribed?: () => void 264 - onPressEditList?: () => void 265 - onPressDeleteList?: () => void 271 + onToggleSubscribed: () => void 272 + onPressEditList: () => void 273 + onPressDeleteList: () => void 274 + onPressShareList: () => void 266 275 }) => { 267 276 const pal = usePalette('default') 268 277 const store = useStores() ··· 301 310 /> 302 311 )} 303 312 {isDesktopWeb && ( 304 - <View style={styles.headerBtns}> 305 - {list.viewer?.muted ? ( 306 - <Button 307 - type="inverted" 308 - label="Unsubscribe" 309 - accessibilityLabel="Unsubscribe" 310 - accessibilityHint="" 311 - onPress={onToggleSubscribed} 312 - /> 313 - ) : ( 314 - <Button 315 - type="primary" 316 - label="Subscribe & Mute" 317 - accessibilityLabel="Subscribe and mute" 318 - accessibilityHint="" 319 - onPress={onToggleSubscribed} 320 - /> 321 - )} 322 - {isOwner && ( 323 - <Button 324 - type="default" 325 - label="Edit List" 326 - accessibilityLabel="Edit list" 327 - accessibilityHint="" 328 - onPress={onPressEditList} 329 - /> 330 - )} 331 - {isOwner && ( 332 - <Button 333 - type="default" 334 - label="Delete List" 335 - accessibilityLabel="Delete list" 336 - accessibilityHint="" 337 - onPress={onPressDeleteList} 338 - /> 339 - )} 340 - </View> 313 + <ListActions 314 + isOwner={isOwner} 315 + muted={list.viewer?.muted} 316 + onPressDeleteList={onPressDeleteList} 317 + onPressEditList={onPressEditList} 318 + onToggleSubscribed={onToggleSubscribed} 319 + onPressShareList={onPressShareList} 320 + /> 341 321 )} 342 322 </View> 343 323 <View>
+10 -3
src/view/com/lists/ListsList.tsx
··· 6 6 StyleSheet, 7 7 View, 8 8 ViewStyle, 9 + FlatList, 9 10 } from 'react-native' 10 11 import {observer} from 'mobx-react-lite' 11 12 import { ··· 13 14 FontAwesomeIconStyle, 14 15 } from '@fortawesome/react-native-fontawesome' 15 16 import {AppBskyGraphDefs as GraphDefs} from '@atproto/api' 16 - import {FlatList} from '../util/Views' 17 17 import {ListCard} from './ListCard' 18 18 import {ProfileCardFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' 19 19 import {ErrorMessage} from '../util/error/ErrorMessage' ··· 149 149 return renderItem ? ( 150 150 renderItem(item) 151 151 ) : ( 152 - <ListCard list={item} testID={`list-${item.name}`} /> 152 + <ListCard 153 + list={item} 154 + testID={`list-${item.name}`} 155 + style={styles.item} 156 + /> 153 157 ) 154 158 }, 155 159 [ ··· 193 197 progressViewOffset={headerOffset} 194 198 /> 195 199 } 196 - contentContainerStyle={s.contentContainer} 200 + contentContainerStyle={[s.contentContainer]} 197 201 style={{paddingTop: headerOffset}} 198 202 onEndReached={onEndReached} 199 203 onEndReachedThreshold={0.6} ··· 237 241 gap: 8, 238 242 }, 239 243 feedFooter: {paddingTop: 20}, 244 + item: { 245 + paddingHorizontal: 18, 246 + }, 240 247 })
+1
src/view/com/util/images/ImageHorzList.tsx
··· 13 13 <View style={[styles.flexRow, style]}> 14 14 {images.map(({thumb, alt}) => ( 15 15 <Image 16 + key={thumb} 16 17 source={{uri: thumb}} 17 18 style={styles.image} 18 19 accessible={true}
+37
src/view/com/util/post-embeds/CustomFeedEmbed.tsx
··· 1 + import React, {useMemo} from 'react' 2 + import {AppBskyFeedDefs} from '@atproto/api' 3 + import {usePalette} from 'lib/hooks/usePalette' 4 + import {StyleSheet} from 'react-native' 5 + import {useStores} from 'state/index' 6 + import {CustomFeedModel} from 'state/models/feeds/custom-feed' 7 + import {CustomFeed} from 'view/com/feeds/CustomFeed' 8 + 9 + export function CustomFeedEmbed({ 10 + record, 11 + }: { 12 + record: AppBskyFeedDefs.GeneratorView 13 + }) { 14 + const pal = usePalette('default') 15 + const store = useStores() 16 + const item = useMemo( 17 + () => new CustomFeedModel(store, record), 18 + [store, record], 19 + ) 20 + return ( 21 + <CustomFeed 22 + item={item} 23 + style={[pal.view, pal.border, styles.customFeedOuter]} 24 + showLikes 25 + /> 26 + ) 27 + } 28 + 29 + const styles = StyleSheet.create({ 30 + customFeedOuter: { 31 + borderWidth: 1, 32 + borderRadius: 8, 33 + marginTop: 4, 34 + paddingHorizontal: 12, 35 + paddingVertical: 12, 36 + }, 37 + })
+35
src/view/com/util/post-embeds/ListEmbed.tsx
··· 1 + import React from 'react' 2 + import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3 + import {usePalette} from 'lib/hooks/usePalette' 4 + import {observer} from 'mobx-react-lite' 5 + import {ListCard} from 'view/com/lists/ListCard' 6 + import {AppBskyGraphDefs} from '@atproto/api' 7 + import {s} from 'lib/styles' 8 + 9 + export const ListEmbed = observer( 10 + ({ 11 + item, 12 + style, 13 + }: { 14 + item: AppBskyGraphDefs.ListView 15 + style?: StyleProp<ViewStyle> 16 + }) => { 17 + const pal = usePalette('default') 18 + 19 + return ( 20 + <View style={[pal.view, pal.border, s.border1, styles.container]}> 21 + <ListCard list={item} style={[style, styles.card]} /> 22 + </View> 23 + ) 24 + }, 25 + ) 26 + 27 + const styles = StyleSheet.create({ 28 + container: { 29 + borderRadius: 8, 30 + }, 31 + card: { 32 + borderTopWidth: 0, 33 + borderRadius: 8, 34 + }, 35 + })
+20 -34
src/view/com/util/post-embeds/index.tsx
··· 14 14 AppBskyEmbedRecordWithMedia, 15 15 AppBskyFeedPost, 16 16 AppBskyFeedDefs, 17 + AppBskyGraphDefs, 17 18 } from '@atproto/api' 18 19 import {Link} from '../Link' 19 20 import {ImageLayoutGrid} from '../images/ImageLayoutGrid' ··· 25 26 import {getYoutubeVideoId} from 'lib/strings/url-helpers' 26 27 import QuoteEmbed from './QuoteEmbed' 27 28 import {AutoSizedImage} from '../images/AutoSizedImage' 28 - import {CustomFeed} from 'view/com/feeds/CustomFeed' 29 - import {CustomFeedModel} from 'state/models/feeds/custom-feed' 29 + import {CustomFeedEmbed} from './CustomFeedEmbed' 30 + import {ListEmbed} from './ListEmbed' 30 31 31 32 type Embed = 32 33 | AppBskyEmbedRecord.View ··· 144 145 } 145 146 } 146 147 148 + // custom feed embed (i.e. generator view) 149 + // = 150 + if ( 151 + AppBskyEmbedRecord.isView(embed) && 152 + AppBskyFeedDefs.isGeneratorView(embed.record) 153 + ) { 154 + return <CustomFeedEmbed record={embed.record} /> 155 + } 156 + 157 + // list embed (e.g. mute lists; i.e. ListView) 158 + if ( 159 + AppBskyEmbedRecord.isView(embed) && 160 + AppBskyGraphDefs.isListView(embed.record) 161 + ) { 162 + return <ListEmbed item={embed.record} /> 163 + } 164 + 147 165 // external link embed 148 166 // = 149 167 if (AppBskyEmbedExternal.isView(embed)) { ··· 164 182 ) 165 183 } 166 184 167 - // custom feed embed (i.e. generator view) 168 - // = 169 - if ( 170 - AppBskyEmbedRecord.isView(embed) && 171 - AppBskyFeedDefs.isGeneratorView(embed.record) 172 - ) { 173 - return <CustomFeedEmbed record={embed.record} /> 174 - } 175 - 176 185 return <View /> 177 186 } 178 187 179 - function CustomFeedEmbed({record}: {record: AppBskyFeedDefs.GeneratorView}) { 180 - const pal = usePalette('default') 181 - const store = useStores() 182 - const item = React.useMemo( 183 - () => new CustomFeedModel(store, record), 184 - [store, record], 185 - ) 186 - return ( 187 - <CustomFeed 188 - item={item} 189 - style={[pal.view, pal.border, styles.customFeedOuter]} 190 - showLikes 191 - /> 192 - ) 193 - } 194 - 195 188 const styles = StyleSheet.create({ 196 189 stackContainer: { 197 190 gap: 6, ··· 207 200 borderWidth: 1, 208 201 borderRadius: 8, 209 202 marginTop: 4, 210 - }, 211 - customFeedOuter: { 212 - borderWidth: 1, 213 - borderRadius: 8, 214 - marginTop: 4, 215 - paddingHorizontal: 12, 216 - paddingVertical: 12, 217 203 }, 218 204 alt: { 219 205 backgroundColor: 'rgba(0, 0, 0, 0.75)',
+1 -1
src/view/screens/ModerationMuteLists.tsx
··· 87 87 <CenteredView 88 88 style={[ 89 89 styles.container, 90 - isDesktopWeb && styles.containerDesktop, 91 90 pal.view, 92 91 pal.border, 92 + isDesktopWeb && styles.containerDesktop, 93 93 ]} 94 94 testID="moderationMutelistsScreen"> 95 95 <ViewHeader
+22 -49
src/view/screens/ProfileList.tsx
··· 1 1 import React from 'react' 2 - import {StyleSheet, View} from 'react-native' 2 + import {StyleSheet} from 'react-native' 3 3 import {useFocusEffect} from '@react-navigation/native' 4 4 import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' 5 5 import {useNavigation} from '@react-navigation/native' ··· 9 9 import {CenteredView} from 'view/com/util/Views' 10 10 import {ListItems} from 'view/com/lists/ListItems' 11 11 import {EmptyState} from 'view/com/util/EmptyState' 12 - import {Button} from 'view/com/util/forms/Button' 13 12 import * as Toast from 'view/com/util/Toast' 14 13 import {ListModel} from 'state/models/content/list' 15 14 import {useStores} from 'state/index' ··· 17 16 import {useSetTitle} from 'lib/hooks/useSetTitle' 18 17 import {NavigationProp} from 'lib/routes/types' 19 18 import {isDesktopWeb} from 'platform/detection' 19 + import {toShareUrl} from 'lib/strings/url-helpers' 20 + import {shareUrl} from 'lib/sharing' 21 + import {ListActions} from 'view/com/lists/ListActions' 20 22 21 23 type Props = NativeStackScreenProps<CommonNavigatorParams, 'ProfileList'> 22 24 export const ProfileListScreen = withAuthRequired( ··· 71 73 store.shell.openModal({ 72 74 name: 'confirm', 73 75 title: 'Delete List', 74 - message: 'Are you sure?', 76 + message: 'Are you sure', 75 77 async onPressConfirm() { 76 78 await list.delete() 77 79 if (navigation.canGoBack()) { ··· 83 85 }) 84 86 }, [store, list, navigation]) 85 87 88 + const onPressShareList = React.useCallback(() => { 89 + const url = toShareUrl(`/profile/${name}/lists/${rkey}`) 90 + shareUrl(url) 91 + }, [name, rkey]) 92 + 86 93 const renderEmptyState = React.useCallback(() => { 87 94 return <EmptyState icon="users-slash" message="This list is empty!" /> 88 95 }, []) 89 96 90 97 const renderHeaderBtns = React.useCallback(() => { 91 98 return ( 92 - <View style={styles.headerBtns}> 93 - {list?.isOwner && ( 94 - <Button 95 - type="default" 96 - label="Delete List" 97 - testID="deleteListBtn" 98 - accessibilityLabel="Delete list" 99 - accessibilityHint="" 100 - onPress={onPressDeleteList} 101 - /> 102 - )} 103 - {list?.isOwner && ( 104 - <Button 105 - type="default" 106 - label="Edit List" 107 - testID="editListBtn" 108 - accessibilityLabel="Edit list" 109 - accessibilityHint="" 110 - onPress={onPressEditList} 111 - /> 112 - )} 113 - {list.list?.viewer?.muted ? ( 114 - <Button 115 - type="inverted" 116 - label="Unsubscribe" 117 - testID="unsubscribeListBtn" 118 - accessibilityLabel="Unsubscribe from list" 119 - accessibilityHint="" 120 - onPress={onToggleSubscribed} 121 - /> 122 - ) : ( 123 - <Button 124 - type="primary" 125 - label="Subscribe & Mute" 126 - testID="subscribeListBtn" 127 - accessibilityLabel="Subscribe to this list" 128 - accessibilityHint="Mutes the users included in this list" 129 - onPress={onToggleSubscribed} 130 - /> 131 - )} 132 - </View> 99 + <ListActions 100 + muted={list.list?.viewer?.muted} 101 + isOwner={list.isOwner} 102 + onPressDeleteList={onPressDeleteList} 103 + onPressEditList={onPressEditList} 104 + onToggleSubscribed={onToggleSubscribed} 105 + onPressShareList={onPressShareList} 106 + reversed={true} 107 + /> 133 108 ) 134 109 }, [ 135 - list?.isOwner, 110 + list.isOwner, 136 111 list.list?.viewer?.muted, 137 112 onPressDeleteList, 138 113 onPressEditList, 114 + onPressShareList, 139 115 onToggleSubscribed, 140 116 ]) 141 117 ··· 155 131 onToggleSubscribed={onToggleSubscribed} 156 132 onPressEditList={onPressEditList} 157 133 onPressDeleteList={onPressDeleteList} 134 + onPressShareList={onPressShareList} 158 135 /> 159 136 </CenteredView> 160 137 ) ··· 162 139 ) 163 140 164 141 const styles = StyleSheet.create({ 165 - headerBtns: { 166 - flexDirection: 'row', 167 - gap: 8, 168 - }, 169 142 container: { 170 143 flex: 1, 171 144 paddingBottom: isDesktopWeb ? 0 : 100,
+1
src/view/shell/bottom-bar/BottomBar.tsx
··· 189 189 pal.text, 190 190 styles.profileIcon, 191 191 styles.onProfile, 192 + {borderColor: pal.text.color}, 192 193 ]}> 193 194 <UserAvatar avatar={store.me.avatar} size={27} /> 194 195 </View>
-1
src/view/shell/bottom-bar/BottomBarStyles.tsx
··· 59 59 top: -4, 60 60 }, 61 61 onProfile: { 62 - borderColor: colors.black, 63 62 borderWidth: 1, 64 63 borderRadius: 100, 65 64 },
+4 -4
yarn.lock
··· 40 40 tlds "^1.234.0" 41 41 typed-emitter "^2.1.0" 42 42 43 - "@atproto/api@0.3.8": 44 - version "0.3.8" 45 - resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.3.8.tgz#3fc0ebd092cc212c2d0b31a600fe1945a02f9cf7" 46 - integrity sha512-7qaIZGEP5J9FW4z8bXezzAmLRzHSXXHo6bWP9Jyu5MLp8tYt9vG6yR2N0QA7GvO0xSYqP87Q5vblPjYXGqtDKg== 43 + "@atproto/api@^0.3.13": 44 + version "0.3.13" 45 + resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.3.13.tgz#e5ccaa83bb909e662286cdf74a77a76de6562a47" 46 + integrity sha512-smDlomgipca16G+jKXAZSMfsAmA5wG8WR3Z1dj29ZShVJlhs6+HHdxX7dWVDYEdSeb2rp/wyHN/tQhxGDAkz/g== 47 47 dependencies: 48 48 "@atproto/common-web" "*" 49 49 "@atproto/uri" "*"