Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Fix all type errors

+377 -294
+1 -1
__mocks__/@gorhom/bottom-sheet.tsx
··· 6 6 const BottomSheetModalProvider = (props: any) => { 7 7 return <BottomSheetModalContext.Provider {...props} value={{}} /> 8 8 } 9 - class BottomSheet extends React.Component { 9 + class BottomSheet extends React.Component<{onClose?: () => void}> { 10 10 snapToIndex() {} 11 11 snapToPosition() {} 12 12 expand() {}
+15 -96
__mocks__/state-mock.ts
··· 9 9 import {OnboardModel} from '../src/state/models/onboard' 10 10 import {ProfilesViewModel} from '../src/state/models/profiles-view' 11 11 import {LinkMetasViewModel} from '../src/state/models/link-metas-view' 12 - import {MembershipsViewModel} from '../src/state/models/memberships-view' 13 12 import {FeedModel} from '../src/state/models/feed-view' 14 13 import {NotificationsViewModel} from '../src/state/models/notifications-view' 15 14 import {ProfileViewModel} from '../src/state/models/profile-view' 16 - import {MembersViewModel} from '../src/state/models/members-view' 17 15 import {ProfileUiModel, Sections} from '../src/state/models/profile-ui' 18 16 import {SessionServiceClient} from '@atproto/api' 19 17 import {UserAutocompleteViewModel} from '../src/state/models/user-autocomplete-view' ··· 70 68 // unknown required because of the missing private methods: _xLoading, _xIdle, _load, _replaceAll 71 69 } as unknown as ProfileViewModel 72 70 73 - export const mockedMembersStore = { 74 - isLoading: false, 75 - isRefreshing: false, 76 - hasLoaded: true, 77 - error: '', 78 - params: { 79 - actor: 'test actor', 80 - }, 81 - subject: { 82 - did: 'test did', 83 - handle: '', 84 - displayName: '', 85 - declaration: { 86 - cid: '', 87 - actorType: '', 88 - }, 89 - avatar: undefined, 90 - }, 91 - members: [ 92 - { 93 - did: 'test did2', 94 - declaration: { 95 - cid: '', 96 - actorType: '', 97 - }, 98 - handle: 'testhandle', 99 - displayName: 'test name', 100 - indexedAt: '', 101 - }, 102 - ], 103 - rootStore: {} as RootStoreModel, 104 - hasContent: true, 105 - hasError: false, 106 - isEmpty: false, 107 - isMember: jest.fn(), 108 - setup: jest.fn().mockResolvedValue({aborted: false}), 109 - refresh: jest.fn().mockResolvedValue({}), 110 - loadMore: jest.fn(), 111 - removeMember: jest.fn(), 112 - // unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append 113 - } as unknown as MembersViewModel 114 - 115 - export const mockedMembershipsStore = { 116 - isLoading: false, 117 - isRefreshing: false, 118 - hasLoaded: true, 119 - error: '', 120 - params: { 121 - actor: '', 122 - limit: 1, 123 - before: '', 124 - }, 125 - subject: { 126 - did: 'test did', 127 - handle: '', 128 - displayName: '', 129 - declaration: {cid: '', actorType: ''}, 130 - avatar: undefined, 131 - }, 132 - memberships: [ 133 - { 134 - did: 'test did', 135 - declaration: { 136 - cid: '', 137 - actorType: 'app.bsky.system.actorUser', 138 - }, 139 - handle: ',', 140 - displayName: '', 141 - createdAt: '', 142 - indexedAt: '', 143 - _reactKey: 'item-1', 144 - }, 145 - ], 146 - rootStore: {} as RootStoreModel, 147 - hasContent: true, 148 - hasError: false, 149 - isEmpty: false, 150 - isMemberOf: jest.fn(), 151 - setup: jest.fn().mockResolvedValue({aborted: false}), 152 - refresh: jest.fn().mockResolvedValue({}), 153 - loadMore: jest.fn(), 154 - // unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append 155 - } as unknown as MembershipsViewModel 156 - 157 71 export const mockedFeedItemStore = { 158 72 _reactKey: 'item-1', 159 73 _isThreadParent: false, 160 74 _isThreadChildElided: false, 161 75 _isThreadChild: false, 76 + _hideParent: false, 77 + _isRenderingAsThread: false, 162 78 post: { 163 79 uri: 'testuri', 164 80 cid: 'test cid', ··· 475 391 export const mockedNavigationTabStore = { 476 392 serialize: jest.fn(), 477 393 hydrate: jest.fn(), 478 - id: 0, 394 + id: '0', 479 395 history: [ 480 396 { 481 397 url: '', 482 398 ts: 0, 483 399 title: '', 484 - id: 0, 400 + id: '0', 485 401 }, 486 402 ], 487 403 index: 0, ··· 490 406 url: '', 491 407 ts: 0, 492 408 title: '', 493 - id: 0, 409 + id: '0', 494 410 }, 495 411 canGoBack: false, 496 412 canGoForward: false, ··· 499 415 url: '', 500 416 title: '', 501 417 index: 0, 502 - id: 0, 418 + id: '0', 503 419 }, 504 420 ], 505 421 forwardTen: [ ··· 507 423 url: '', 508 424 title: '', 509 425 index: 0, 510 - id: 0, 426 + id: '0', 511 427 }, 512 428 ], 513 429 navigate: jest.fn(), ··· 524 440 url: '/', 525 441 title: '', 526 442 index: 1, 527 - id: 1, 443 + id: '1', 528 444 }, 529 445 ], 530 446 getForwardList: jest.fn(), ··· 582 498 avatar: '', 583 499 notificationCount: 0, 584 500 rootStore: {} as RootStoreModel, 585 - memberships: mockedMembershipsStore, 586 501 mainFeed: mockedFeedStore, 587 502 notifications: mockedNotificationsStore, 588 503 clear: jest.fn(), 589 504 load: jest.fn(), 590 505 clearNotificationCount: jest.fn(), 591 506 fetchNotifications: jest.fn(), 507 + bgFetchNotifications: jest.fn(), 592 508 refreshMemberships: jest.fn(), 593 509 } as MeModel 594 510 ··· 650 566 hydrate: jest.fn(), 651 567 fetchStateUpdate: jest.fn(), 652 568 clearAll: jest.fn(), 569 + onPostDeleted: jest.fn(), 570 + emitPostDeleted: jest.fn(), 571 + initBgFetch: jest.fn(), 572 + onBgFetch: jest.fn(), 573 + onBgFetchTimeout: jest.fn(), 653 574 session: mockedSessionStore, 654 575 nav: mockedNavigationStore, 655 576 shell: mockedShellStore, ··· 663 584 export const mockedProfileUiStore = { 664 585 profile: mockedProfileStore, 665 586 feed: mockedFeedStore, 666 - memberships: mockedMembershipsStore, 667 - members: mockedMembersStore, 668 587 selectedViewIndex: 0, 669 588 rootStore: mockedRootStore, 670 589 params: { ··· 675 594 isRefreshing: false, 676 595 isUser: true, 677 596 isScene: false, 678 - selectorItems: [Sections.Posts, Sections.PostsWithReplies, Sections.Scenes], 597 + selectorItems: [Sections.Posts, Sections.PostsWithReplies], 679 598 selectedView: Sections.Posts, 680 599 setSelectedViewIndex: jest.fn(), 681 600 setup: jest.fn().mockResolvedValue({aborted: false}),
+4
__tests__/lib/extractHtmlMeta.test.ts
··· 41 41 42 42 it.each(cases)( 43 43 'given the html tag %p, returns %p', 44 + // @ts-ignore not worth fixing -prf 44 45 (input, expectedResult) => { 45 46 const output = extractHtmlMeta({html: input as string, hostname: ''}) 46 47 expect(output).toEqual(expectedResult) ··· 86 87 title: '@bluesky on Twitter', 87 88 } 88 89 const output = extractHtmlMeta({ 90 + html: '', 89 91 hostname: 'twitter.com', 90 92 pathname: '/bluesky', 91 93 }) ··· 97 99 title: 'Tweet by @bluesky', 98 100 } 99 101 const output = extractHtmlMeta({ 102 + html: '', 100 103 hostname: 'twitter.com', 101 104 pathname: '/bluesky/status/1582437529969917953', 102 105 }) ··· 108 111 title: 'Twitter', 109 112 } 110 113 const output = extractHtmlMeta({ 114 + html: '', 111 115 hostname: 'twitter.com', 112 116 pathname: '/i/articles/follows/-1675653703?time_window=24', 113 117 })
+1 -1
__tests__/view/lib/useOnMainScroll.test.tsx
··· 5 5 6 6 describe('useOnMainScroll', () => { 7 7 const mockedProps = { 8 - navIdx: [0, 0] as [number, number], 8 + navIdx: '0-0', 9 9 params: {}, 10 10 visible: true, 11 11 }
+1 -1
__tests__/view/screens/Search.test.tsx
··· 5 5 describe('Search', () => { 6 6 jest.useFakeTimers() 7 7 const mockedProps = { 8 - navIdx: [0, 0] as [number, number], 8 + navIdx: '0-0', 9 9 params: { 10 10 name: 'test name', 11 11 },
+1 -1
jest/test-pds.ts
··· 179 179 did: subject.did, 180 180 declarationCid: subject.declarationCid, 181 181 }, 182 - createdAt: date.next().value, 182 + createdAt: date.next().value || '', 183 183 }, 184 184 ) 185 185 }
+2 -2
src/App.web.tsx
··· 2 2 import * as view from './view/index' 3 3 import {RootStoreModel, setupState, RootStoreProvider} from './state' 4 4 import {DesktopWebShell} from './view/shell/desktop-web' 5 - import Toast from 'react-native-root-toast' 5 + // import Toast from 'react-native-root-toast' TODO 6 6 7 7 function App() { 8 8 const [rootStore, setRootStore] = useState<RootStoreModel | undefined>( ··· 23 23 return ( 24 24 <RootStoreProvider value={rootStore}> 25 25 <DesktopWebShell /> 26 - <Toast.ToastContainer /> 27 26 </RootStoreProvider> 28 27 ) 28 + // <Toast.ToastContainer /> TODO 29 29 } 30 30 31 31 export default App
+1 -1
src/lib/extractHtmlMeta.ts
··· 63 63 // Workaround for some websites not having a title or description in the meta tags in the initial serve 64 64 if (isYoutubeUrl) { 65 65 res = {...res, ...extractYoutubeMeta(html)} 66 - } else if (isTwitterUrl) { 66 + } else if (isTwitterUrl && pathname) { 67 67 res = {...extractTwitterMeta({pathname})} 68 68 } 69 69
-2
src/state/models/profile-ui.ts
··· 98 98 const view = this.currentView 99 99 if (view instanceof FeedModel) { 100 100 await view.update() 101 - } else { 102 - await view.refresh() 103 101 } 104 102 } 105 103
+9 -2
src/view/com/composer/ComposePost.tsx
··· 16 16 PasteInputRef, 17 17 } from '@mattermost/react-native-paste-input' 18 18 import LinearGradient from 'react-native-linear-gradient' 19 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 19 + import { 20 + FontAwesomeIcon, 21 + FontAwesomeIconStyle, 22 + } from '@fortawesome/react-native-fontawesome' 20 23 import {useAnalytics} from '@segment/analytics-react-native' 21 24 import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view' 22 25 import {Autocomplete} from './Autocomplete' ··· 438 441 hitSlop={HITSLOP}> 439 442 <FontAwesomeIcon 440 443 icon={['far', 'image']} 441 - style={selectedPhotos.length < 4 ? pal.link : pal.textLight} 444 + style={ 445 + (selectedPhotos.length < 4 446 + ? pal.link 447 + : pal.textLight) as FontAwesomeIconStyle 448 + } 442 449 size={24} 443 450 /> 444 451 </TouchableOpacity>
+14 -3
src/view/com/composer/PhotoCarouselPicker.tsx
··· 1 1 import React, {useCallback} from 'react' 2 2 import {Image, StyleSheet, TouchableOpacity, ScrollView} from 'react-native' 3 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 3 + import { 4 + FontAwesomeIcon, 5 + FontAwesomeIconStyle, 6 + } from '@fortawesome/react-native-fontawesome' 4 7 import { 5 8 openPicker, 6 9 openCamera, ··· 131 134 testID="openCameraButton" 132 135 style={[styles.galleryButton, pal.border, styles.photo]} 133 136 onPress={handleOpenCamera}> 134 - <FontAwesomeIcon icon="camera" size={24} style={pal.link} /> 137 + <FontAwesomeIcon 138 + icon="camera" 139 + size={24} 140 + style={pal.link as FontAwesomeIconStyle} 141 + /> 135 142 </TouchableOpacity> 136 143 <TouchableOpacity 137 144 testID="openGalleryButton" 138 145 style={[styles.galleryButton, pal.border, styles.photo]} 139 146 onPress={handleOpenGallery}> 140 - <FontAwesomeIcon icon="image" style={pal.link} size={24} /> 147 + <FontAwesomeIcon 148 + icon="image" 149 + style={pal.link as FontAwesomeIconStyle} 150 + size={24} 151 + /> 141 152 </TouchableOpacity> 142 153 {localPhotos.photos.map((item: PhotoIdentifier, index: number) => ( 143 154 <TouchableOpacity
+5 -2
src/view/com/discover/SuggestedFollows.tsx
··· 7 7 View, 8 8 } from 'react-native' 9 9 import LinearGradient from 'react-native-linear-gradient' 10 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 10 + import { 11 + FontAwesomeIcon, 12 + FontAwesomeIconStyle, 13 + } from '@fortawesome/react-native-fontawesome' 11 14 import {observer} from 'mobx-react-lite' 12 15 import _omit from 'lodash.omit' 13 16 import {ErrorScreen} from '../util/error/ErrorScreen' ··· 199 202 style={[styles.btn, styles.gradientBtn]}> 200 203 <FontAwesomeIcon 201 204 icon="plus" 202 - style={[s.white, s.mr5]} 205 + style={[s.white as FontAwesomeIconStyle, s.mr5]} 203 206 size={15} 204 207 /> 205 208 <Text style={[s.white, s.fw600, s.f15]}>Follow</Text>
+21
src/view/com/lightbox/ImageViewing/components/ImageItem/ImageItem.tsx
··· 1 + // default implementation fallback for web 2 + 3 + import React from 'react' 4 + import {View} from 'react-native' 5 + import {ImageSource} from '../../@types' 6 + 7 + type Props = { 8 + imageSrc: ImageSource 9 + onRequestClose: () => void 10 + onZoom: (scaled: boolean) => void 11 + onLongPress: (image: ImageSource) => void 12 + delayLongPress: number 13 + swipeToCloseEnabled?: boolean 14 + doubleTapToZoomEnabled?: boolean 15 + } 16 + 17 + const ImageItem = (_props: Props) => { 18 + return <View /> 19 + } 20 + 21 + export default React.memo(ImageItem)
+1 -1
src/view/com/lightbox/ImageViewing/hooks/useImageDimensions.ts
··· 47 47 if (imageDimensions) { 48 48 resolve(imageDimensions) 49 49 } else { 50 - // @ts-ignore 51 50 Image.getSizeWithHeaders( 51 + // @ts-ignore 52 52 source.uri, 53 53 source.headers, 54 54 (width: number, height: number) => {
+1 -1
src/view/com/lightbox/ImageViewing/hooks/usePanResponder.ts
··· 61 61 let tmpTranslate: Position | null = null 62 62 let isDoubleTapPerformed = false 63 63 let lastTapTS: number | null = null 64 - let longPressHandlerRef: number | null = null 64 + let longPressHandlerRef: NodeJS.Timeout | null = null 65 65 66 66 const meaningfulShift = MIN_DIMENSION * 0.01 67 67 const scaleValue = new Animated.Value(initialScale)
+1
src/view/com/lightbox/ImageViewing/utils.ts
··· 77 77 const transform = translate.getTranslateTransform() 78 78 79 79 if (scale) { 80 + // @ts-ignore TODO - is scale incorrect? might need to remove -prf 80 81 transform.push({scale}, {perspective: new Animated.Value(1000)}) 81 82 } 82 83
+9 -2
src/view/com/lightbox/Lightbox.tsx
··· 5 5 import {useStores} from '../../../state' 6 6 import * as models from '../../../state/models/shell-ui' 7 7 import {saveImageModal} from '../../../lib/images' 8 + import {ImageSource} from './ImageViewing/@types' 8 9 9 10 export const Lightbox = observer(function Lightbox() { 10 11 const store = useStores() ··· 15 16 const onClose = () => { 16 17 store.shell.closeLightbox() 17 18 } 18 - const onLongPress = ({uri}: {uri: string}) => { 19 - saveImageModal({uri}) 19 + const onLongPress = (image: ImageSource) => { 20 + if ( 21 + typeof image === 'object' && 22 + 'uri' in image && 23 + typeof image.uri === 'string' 24 + ) { 25 + saveImageModal({uri: image.uri}) 26 + } 20 27 } 21 28 22 29 if (store.shell.activeLightbox?.name === 'profile-image') {
+10 -3
src/view/com/login/CreateAccount.tsx
··· 9 9 TouchableOpacity, 10 10 View, 11 11 } from 'react-native' 12 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 12 + import { 13 + FontAwesomeIcon, 14 + FontAwesomeIconStyle, 15 + } from '@fortawesome/react-native-fontawesome' 13 16 import {ComAtprotoAccountCreate} from '@atproto/api' 14 17 import * as EmailValidator from 'email-validator' 15 18 import {useAnalytics} from '@segment/analytics-react-native' ··· 264 267 <Picker 265 268 style={[pal.text, styles.picker]} 266 269 labelStyle={styles.pickerLabel} 267 - iconStyle={pal.textLight} 270 + iconStyle={pal.textLight as FontAwesomeIconStyle} 268 271 value={userDomain} 269 272 items={serviceDescription.availableUserDomains.map(d => ({ 270 273 label: `.${d}`, ··· 371 374 return ( 372 375 <View style={styles.policies}> 373 376 <View style={[styles.errorIcon, {borderColor: pal.colors.text}, s.mt2]}> 374 - <FontAwesomeIcon icon="exclamation" style={pal.textLight} size={10} /> 377 + <FontAwesomeIcon 378 + icon="exclamation" 379 + style={pal.textLight as FontAwesomeIconStyle} 380 + size={10} 381 + /> 375 382 </View> 376 383 <Text style={[pal.textLight, s.pl5, s.flex1]}> 377 384 This service has not provided terms of service or a privacy policy.
+14 -3
src/view/com/login/Signin.tsx
··· 8 8 TouchableOpacity, 9 9 View, 10 10 } from 'react-native' 11 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 11 + import { 12 + FontAwesomeIcon, 13 + FontAwesomeIconStyle, 14 + } from '@fortawesome/react-native-fontawesome' 12 15 import * as EmailValidator from 'email-validator' 13 16 import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api' 14 17 import {useAnalytics} from '@segment/analytics-react-native' ··· 337 340 {toNiceDomain(serviceUrl)} 338 341 </Text> 339 342 <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> 340 - <FontAwesomeIcon icon="pen" size={12} style={pal.textLight} /> 343 + <FontAwesomeIcon 344 + icon="pen" 345 + size={12} 346 + style={pal.textLight as FontAwesomeIconStyle} 347 + /> 341 348 </View> 342 349 </TouchableOpacity> 343 350 </View> ··· 514 521 {toNiceDomain(serviceUrl)} 515 522 </Text> 516 523 <View style={[pal.btn, styles.textBtnFakeInnerBtn]}> 517 - <FontAwesomeIcon icon="pen" size={12} style={pal.text} /> 524 + <FontAwesomeIcon 525 + icon="pen" 526 + size={12} 527 + style={pal.text as FontAwesomeIconStyle} 528 + /> 518 529 </View> 519 530 </TouchableOpacity> 520 531 <View style={[pal.borderDark, styles.groupContent]}>
-1
src/view/com/modals/EditProfile.tsx
··· 108 108 <UserBanner 109 109 banner={userBanner} 110 110 onSelectNewBanner={onSelectNewBanner} 111 - handle={profileView.handle} 112 111 /> 113 112 <View style={styles.avi}> 114 113 <UserAvatar
+2 -10
src/view/com/modals/Modal.tsx
··· 62 62 ) 63 63 } else if (store.shell.activeModal?.name === 'report-post') { 64 64 snapPoints = ReportPostModal.snapPoints 65 - element = ( 66 - <ReportPostModal.Component 67 - {...(store.shell.activeModal as models.ReportPostModal)} 68 - /> 69 - ) 65 + element = <ReportPostModal.Component /> 70 66 } else if (store.shell.activeModal?.name === 'report-account') { 71 67 snapPoints = ReportAccountModal.snapPoints 72 - element = ( 73 - <ReportAccountModal.Component 74 - {...(store.shell.activeModal as models.ReportAccountModal)} 75 - /> 76 - ) 68 + element = <ReportAccountModal.Component /> 77 69 } else { 78 70 element = <View /> 79 71 }
+17 -5
src/view/com/modals/ServerInput.tsx
··· 1 1 import React, {useState} from 'react' 2 2 import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native' 3 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 3 + import { 4 + FontAwesomeIcon, 5 + FontAwesomeIconStyle, 6 + } from '@fortawesome/react-native-fontawesome' 4 7 import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet' 5 8 import {Text} from '../util/text/Text' 6 9 import {useStores} from '../../../state' ··· 37 40 style={styles.btn} 38 41 onPress={() => doSelect(LOCAL_DEV_SERVICE)}> 39 42 <Text style={styles.btnText}>Local dev server</Text> 40 - <FontAwesomeIcon icon="arrow-right" style={s.white} /> 43 + <FontAwesomeIcon 44 + icon="arrow-right" 45 + style={s.white as FontAwesomeIconStyle} 46 + /> 41 47 </TouchableOpacity> 42 48 <TouchableOpacity 43 49 style={styles.btn} 44 50 onPress={() => doSelect(STAGING_SERVICE)}> 45 51 <Text style={styles.btnText}>Staging</Text> 46 - <FontAwesomeIcon icon="arrow-right" style={s.white} /> 52 + <FontAwesomeIcon 53 + icon="arrow-right" 54 + style={s.white as FontAwesomeIconStyle} 55 + /> 47 56 </TouchableOpacity> 48 57 </> 49 58 ) : undefined} ··· 51 60 style={styles.btn} 52 61 onPress={() => doSelect(PROD_SERVICE)}> 53 62 <Text style={styles.btnText}>Bluesky.Social</Text> 54 - <FontAwesomeIcon icon="arrow-right" style={s.white} /> 63 + <FontAwesomeIcon 64 + icon="arrow-right" 65 + style={s.white as FontAwesomeIconStyle} 66 + /> 55 67 </TouchableOpacity> 56 68 </View> 57 69 <View style={styles.group}> ··· 74 86 onPress={() => doSelect(customUrl)}> 75 87 <FontAwesomeIcon 76 88 icon="check" 77 - style={[s.black, styles.checkIcon]} 89 + style={[s.black as FontAwesomeIconStyle, styles.checkIcon]} 78 90 size={18} 79 91 /> 80 92 </TouchableOpacity>
+21 -6
src/view/com/notifications/FeedItem.tsx
··· 9 9 } from 'react-native' 10 10 import {AppBskyEmbedImages} from '@atproto/api' 11 11 import {AtUri} from '../../../third-party/uri' 12 - import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome' 12 + import { 13 + FontAwesomeIcon, 14 + FontAwesomeIconStyle, 15 + Props, 16 + } from '@fortawesome/react-native-fontawesome' 13 17 import {NotificationsViewItemModel} from '../../../state/models/notifications-view' 14 18 import {PostThreadViewModel} from '../../../state/models/post-thread-view' 15 19 import {s, colors} from '../../lib/styles' ··· 98 102 if (item.isUpvote) { 99 103 action = 'liked your post' 100 104 icon = 'HeartIconSolid' 101 - iconStyle = [s.red3, {position: 'relative', top: -4}] 105 + iconStyle = [ 106 + s.red3 as FontAwesomeIconStyle, 107 + {position: 'relative', top: -4}, 108 + ] 102 109 } else if (item.isRepost) { 103 110 action = 'reposted your post' 104 111 icon = 'retweet' 105 - iconStyle = [s.green3] 112 + iconStyle = [s.green3 as FontAwesomeIconStyle] 106 113 } else if (item.isReply) { 107 114 action = 'replied to your post' 108 115 icon = ['far', 'comment'] 109 116 } else if (item.isFollow) { 110 117 action = 'followed you' 111 118 icon = 'user-plus' 112 - iconStyle = [s.blue3] 119 + iconStyle = [s.blue3 as FontAwesomeIconStyle] 113 120 } else { 114 121 return <></> 115 122 } ··· 292 299 authors.length * (EXPANDED_AUTHOR_EL_HEIGHT + 10) /*10=margin*/ 293 300 const heightStyle = { 294 301 height: Animated.multiply(heightInterp, targetHeight), 295 - overflow: 'hidden', 296 302 } 297 303 React.useEffect(() => { 298 304 Animated.timing(heightInterp, { ··· 302 308 }).start() 303 309 }, [heightInterp, visible]) 304 310 return ( 305 - <Animated.View style={[heightStyle, visible ? s.mb10 : undefined]}> 311 + <Animated.View 312 + style={[ 313 + heightStyle, 314 + styles.overflowHidden, 315 + visible ? s.mb10 : undefined, 316 + ]}> 306 317 {authors.map(author => ( 307 318 <Link 308 319 key={author.href} ··· 360 371 } 361 372 362 373 const styles = StyleSheet.create({ 374 + overflowHidden: { 375 + overflow: 'hidden', 376 + }, 377 + 363 378 outer: { 364 379 padding: 10, 365 380 paddingRight: 15,
+16 -10
src/view/com/onboard/FeatureExplainer.tsx
··· 9 9 View, 10 10 } from 'react-native' 11 11 import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view' 12 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 12 + import { 13 + FontAwesomeIcon, 14 + FontAwesomeIconStyle, 15 + } from '@fortawesome/react-native-fontawesome' 13 16 import {Text} from '../util/text/Text' 14 17 import {useStores} from '../../../state' 15 18 import {s} from '../../lib/styles' 16 19 import {TABS_EXPLAINER} from '../../lib/assets' 17 20 import {TABS_ENABLED} from '../../../build-flags' 21 + 22 + const ROUTES = TABS_ENABLED 23 + ? [ 24 + {key: 'intro', title: 'Intro'}, 25 + {key: 'tabs', title: 'Tabs'}, 26 + ] 27 + : [{key: 'intro', title: 'Intro'}] 18 28 19 29 const Intro = () => ( 20 30 <View style={styles.explainer}> ··· 37 47 <View style={s.flex1} /> 38 48 <FontAwesomeIcon 39 49 icon={['far', 'clone']} 40 - style={[s.black, s.mb5]} 50 + style={[s.black as FontAwesomeIconStyle, s.mb5]} 41 51 size={36} 42 52 /> 43 53 <View style={s.flex1} /> ··· 62 72 const layout = useWindowDimensions() 63 73 const store = useStores() 64 74 const [index, setIndex] = useState(0) 65 - const routes = [ 66 - {key: 'intro', title: 'Intro'}, 67 - TABS_ENABLED ? {key: 'tabs', title: 'Tabs'} : undefined, 68 - ].filter(Boolean) 69 75 70 76 const onPressSkip = () => store.onboard.next() 71 77 const onPressNext = () => { 72 - if (index >= routes.length - 1) { 78 + if (index >= ROUTES.length - 1) { 73 79 store.onboard.next() 74 80 } else { 75 81 setIndex(index + 1) ··· 103 109 ) 104 110 } 105 111 106 - const FirstExplainer = SCENE_MAP[routes[0]?.key as keyof typeof SCENE_MAP] 112 + const FirstExplainer = SCENE_MAP[ROUTES[0]?.key as keyof typeof SCENE_MAP] 107 113 return ( 108 114 <SafeAreaView style={styles.container}> 109 - {routes.length > 1 ? ( 115 + {ROUTES.length > 1 ? ( 110 116 <TabView 111 - navigationState={{index, routes}} 117 + navigationState={{index, routes: ROUTES}} 112 118 renderScene={renderScene} 113 119 renderTabBar={renderTabBar} 114 120 onIndexChange={setIndex}
+9 -3
src/view/com/post-thread/PostThreadItem.tsx
··· 3 3 import {StyleSheet, View} from 'react-native' 4 4 import Clipboard from '@react-native-clipboard/clipboard' 5 5 import {AtUri} from '../../../third-party/uri' 6 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 6 + import { 7 + FontAwesomeIcon, 8 + FontAwesomeIconStyle, 9 + } from '@fortawesome/react-native-fontawesome' 7 10 import {PostThreadViewPostModel} from '../../../state/models/post-thread-view' 8 11 import {Link} from '../util/Link' 9 12 import {RichText} from '../util/text/RichText' ··· 59 62 replyTo: { 60 63 uri: item.post.uri, 61 64 cid: item.post.cid, 62 - text: record.text as string, 65 + text: record?.text as string, 63 66 author: { 64 67 handle: item.post.author.handle, 65 68 displayName: item.post.author.displayName, ··· 103 106 if (deleted) { 104 107 return ( 105 108 <View style={[styles.outer, pal.border, pal.view, s.p20, s.flexRow]}> 106 - <FontAwesomeIcon icon={['far', 'trash-can']} style={pal.icon} /> 109 + <FontAwesomeIcon 110 + icon={['far', 'trash-can']} 111 + style={pal.icon as FontAwesomeIconStyle} 112 + /> 107 113 <Text style={[pal.textLight, s.ml10]}>This post has been deleted.</Text> 108 114 </View> 109 115 )
+3 -3
src/view/com/post-thread/PostVotedBy.tsx
··· 1 1 import React, {useEffect} from 'react' 2 2 import {observer} from 'mobx-react-lite' 3 3 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native' 4 - import {VotesViewModel, VotesItem} from '../../../state/models/votes-view' 4 + import {VotesViewModel, VoteItem} from '../../../state/models/votes-view' 5 5 import {Link} from '../util/Link' 6 6 import {Text} from '../util/text/Text' 7 7 import {ErrorMessage} from '../util/error/ErrorMessage' ··· 56 56 57 57 // loaded 58 58 // = 59 - const renderItem = ({item}: {item: VotesItem}) => <LikedByItem item={item} /> 59 + const renderItem = ({item}: {item: VoteItem}) => <LikedByItem item={item} /> 60 60 return ( 61 61 <FlatList 62 62 data={view.votes} ··· 76 76 ) 77 77 }) 78 78 79 - const LikedByItem = ({item}: {item: VotesItem}) => { 79 + const LikedByItem = ({item}: {item: VoteItem}) => { 80 80 const pal = usePalette('default') 81 81 82 82 return (
+2 -2
src/view/com/post/PostText.tsx
··· 1 1 import React, {useState, useEffect} from 'react' 2 2 import {observer} from 'mobx-react-lite' 3 - import {StyleSheet, View} from 'react-native' 3 + import {StyleProp, StyleSheet, TextStyle, View} from 'react-native' 4 4 import {LoadingPlaceholder} from '../util/LoadingPlaceholder' 5 5 import {ErrorMessage} from '../util/error/ErrorMessage' 6 6 import {Text} from '../util/text/Text' ··· 12 12 style, 13 13 }: { 14 14 uri: string 15 - style?: StyleProp 15 + style?: StyleProp<TextStyle> 16 16 }) { 17 17 const store = useStores() 18 18 const [model, setModel] = useState<PostModel | undefined>()
+12 -3
src/view/com/posts/FeedItem.tsx
··· 4 4 import Clipboard from '@react-native-clipboard/clipboard' 5 5 import Svg, {Circle, Line} from 'react-native-svg' 6 6 import {AtUri} from '../../../third-party/uri' 7 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 7 + import { 8 + FontAwesomeIcon, 9 + FontAwesomeIconStyle, 10 + } from '@fortawesome/react-native-fontawesome' 8 11 import {FeedItemModel} from '../../../state/models/feed-view' 9 12 import {Link} from '../util/Link' 10 13 import {Text} from '../util/text/Text' ··· 137 140 }> 138 141 <FontAwesomeIcon 139 142 icon="retweet" 140 - style={[styles.includeReasonIcon, {color: pal.colors.textLight}]} 143 + style={[ 144 + styles.includeReasonIcon, 145 + {color: pal.colors.textLight} as FontAwesomeIconStyle, 146 + ]} 141 147 /> 142 148 <Text type="sm-bold" style={pal.textLight}> 143 149 Reposted by{' '} ··· 167 173 <FontAwesomeIcon 168 174 icon="reply" 169 175 size={9} 170 - style={[{color: pal.colors.textLight}, s.mr5]} 176 + style={[ 177 + {color: pal.colors.textLight} as FontAwesomeIconStyle, 178 + s.mr5, 179 + ]} 171 180 /> 172 181 <Text type="md" style={[pal.textLight, s.mr2]}> 173 182 Reply to
+5 -1
src/view/com/profile/ProfileFollows.tsx
··· 97 97 size={40} 98 98 displayName={item.displayName} 99 99 handle={item.handle} 100 - avatar={item.avatar} 100 + avatar={ 101 + item.avatar as 102 + | string 103 + | undefined /* HACK: type signature is wrong in the api */ 104 + } 101 105 /> 102 106 </View> 103 107 <View style={styles.layoutContent}>
+9 -3
src/view/com/profile/ProfileHeader.tsx
··· 8 8 View, 9 9 } from 'react-native' 10 10 import LinearGradient from 'react-native-linear-gradient' 11 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 11 + import { 12 + FontAwesomeIcon, 13 + FontAwesomeIconStyle, 14 + } from '@fortawesome/react-native-fontawesome' 12 15 import {BlurView} from '@react-native-community/blur' 13 16 import {ProfileViewModel} from '../../../state/models/profile-view' 14 17 import {useStores} from '../../../state' ··· 142 145 } 143 146 return ( 144 147 <View style={pal.view}> 145 - <UserBanner handle={view.handle} banner={view.banner} /> 148 + <UserBanner banner={view.banner} /> 146 149 <View style={styles.content}> 147 150 <View style={[styles.buttonsLine]}> 148 151 {isMe ? ( ··· 181 184 start={{x: 0, y: 0}} 182 185 end={{x: 1, y: 1}} 183 186 style={[styles.btn, styles.gradientBtn]}> 184 - <FontAwesomeIcon icon="plus" style={[s.white, s.mr5]} /> 187 + <FontAwesomeIcon 188 + icon="plus" 189 + style={[s.white as FontAwesomeIconStyle, s.mr5]} 190 + /> 185 191 <Text type="button" style={[s.white, s.bold]}> 186 192 Follow 187 193 </Text>
+8 -2
src/view/com/util/EmptyState.tsx
··· 1 1 import React from 'react' 2 2 import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' 3 3 import {IconProp} from '@fortawesome/fontawesome-svg-core' 4 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 4 + import { 5 + FontAwesomeIcon, 6 + FontAwesomeIconStyle, 7 + } from '@fortawesome/react-native-fontawesome' 5 8 import {Text} from './text/Text' 6 9 import {UserGroupIcon} from '../../lib/icons' 7 10 import {usePalette} from '../../lib/hooks/usePalette' ··· 25 28 <FontAwesomeIcon 26 29 icon={icon} 27 30 size={64} 28 - style={[styles.icon, {color: pal.colors.emptyStateIcon}]} 31 + style={[ 32 + styles.icon, 33 + {color: pal.colors.emptyStateIcon} as FontAwesomeIconStyle, 34 + ]} 29 35 /> 30 36 )} 31 37 </View>
+1 -1
src/view/com/util/LoadingPlaceholder.tsx
··· 63 63 </View> 64 64 <View style={s.flex1}> 65 65 <HeartIcon 66 - style={{color: theme.palette.default.icon}} 66 + style={{color: theme.palette.default.icon} as ViewStyle} 67 67 size={17} 68 68 strokeWidth={1.7} 69 69 />
+13 -5
src/view/com/util/PostCtrls.tsx
··· 7 7 View, 8 8 ViewStyle, 9 9 } from 'react-native' 10 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 10 + import { 11 + FontAwesomeIcon, 12 + FontAwesomeIconStyle, 13 + } from '@fortawesome/react-native-fontawesome' 11 14 import ReactNativeHapticFeedback from 'react-native-haptic-feedback' 12 15 import {Text} from './text/Text' 13 16 import {PostDropdownBtn} from './forms/DropdownButton' ··· 147 150 <Animated.View style={anim1Style}> 148 151 <RepostIcon 149 152 style={ 150 - opts.isReposted ? styles.ctrlIconReposted : defaultCtrlColor 153 + (opts.isReposted 154 + ? styles.ctrlIconReposted 155 + : defaultCtrlColor) as ViewStyle 151 156 } 152 157 strokeWidth={2.4} 153 158 size={opts.big ? 24 : 20} ··· 173 178 <Animated.View style={anim2Style}> 174 179 {opts.isUpvoted ? ( 175 180 <HeartIconSolid 176 - style={[styles.ctrlIconUpvoted]} 181 + style={styles.ctrlIconUpvoted as ViewStyle} 177 182 size={opts.big ? 22 : 16} 178 183 /> 179 184 ) : ( 180 185 <HeartIcon 181 - style={[defaultCtrlColor, opts.big ? styles.mt1 : undefined]} 186 + style={[ 187 + defaultCtrlColor as ViewStyle, 188 + opts.big ? styles.mt1 : undefined, 189 + ]} 182 190 strokeWidth={3} 183 191 size={opts.big ? 20 : 16} 184 192 /> ··· 214 222 { 215 223 color: 216 224 theme.colorScheme === 'light' ? colors.gray4 : colors.gray5, 217 - }, 225 + } as FontAwesomeIconStyle, 218 226 ]} 219 227 /> 220 228 </PostDropdownBtn>
+9 -2
src/view/com/util/ViewHeader.tsx
··· 6 6 TouchableOpacity, 7 7 View, 8 8 } from 'react-native' 9 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 9 + import { 10 + FontAwesomeIcon, 11 + FontAwesomeIconStyle, 12 + } from '@fortawesome/react-native-fontawesome' 10 13 import {UserAvatar} from './UserAvatar' 11 14 import {Text} from './text/Text' 12 15 import {MagnifyingGlassIcon} from '../../lib/icons' ··· 92 95 <ActivityIndicator /> 93 96 ) : ( 94 97 <> 95 - <FontAwesomeIcon icon="signal" style={pal.text} size={16} /> 98 + <FontAwesomeIcon 99 + icon="signal" 100 + style={pal.text as FontAwesomeIconStyle} 101 + size={16} 102 + /> 96 103 <FontAwesomeIcon 97 104 icon="x" 98 105 style={[
+9 -2
src/view/com/util/error/ErrorMessage.tsx
··· 6 6 View, 7 7 ViewStyle, 8 8 } from 'react-native' 9 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 9 + import { 10 + FontAwesomeIcon, 11 + FontAwesomeIconStyle, 12 + } from '@fortawesome/react-native-fontawesome' 10 13 import {Text} from '../text/Text' 11 14 import {useTheme} from '../../../lib/ThemeContext' 12 15 import {usePalette} from '../../../lib/hooks/usePalette' ··· 28 31 <View testID="errorMessageView" style={[styles.outer, pal.view, style]}> 29 32 <View 30 33 style={[styles.errorIcon, {backgroundColor: theme.palette.error.icon}]}> 31 - <FontAwesomeIcon icon="exclamation" style={pal.text} size={16} /> 34 + <FontAwesomeIcon 35 + icon="exclamation" 36 + style={pal.text as FontAwesomeIconStyle} 37 + size={16} 38 + /> 32 39 </View> 33 40 <Text 34 41 type="sm"
+9 -2
src/view/com/util/error/ErrorScreen.tsx
··· 1 1 import React from 'react' 2 2 import {StyleSheet, TouchableOpacity, View} from 'react-native' 3 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 3 + import { 4 + FontAwesomeIcon, 5 + FontAwesomeIconStyle, 6 + } from '@fortawesome/react-native-fontawesome' 4 7 import {Text} from '../text/Text' 5 8 import {colors} from '../../../lib/styles' 6 9 import {useTheme} from '../../../lib/ThemeContext' ··· 58 61 testID="errorScreenTryAgainButton" 59 62 style={[styles.btn, {backgroundColor: theme.palette.error.icon}]} 60 63 onPress={onPressTryAgain}> 61 - <FontAwesomeIcon icon="arrows-rotate" style={pal.text} size={16} /> 64 + <FontAwesomeIcon 65 + icon="arrows-rotate" 66 + style={pal.text as FontAwesomeIconStyle} 67 + size={16} 68 + /> 62 69 <Text type="button" style={[styles.btnText, pal.text]}> 63 70 Try again 64 71 </Text>
+1 -1
src/view/com/util/forms/DropdownButton.tsx
··· 37 37 menuWidth, 38 38 children, 39 39 }: { 40 - type: DropdownButtonType 40 + type?: DropdownButtonType 41 41 style?: StyleProp<ViewStyle> 42 42 items: DropdownItem[] 43 43 label?: string
+2 -1
src/view/com/util/images/AutoSizedImage.tsx
··· 30 30 }: { 31 31 uri: string 32 32 onPress?: () => void 33 + onLongPress?: () => void 33 34 style?: StyleProp<ImageStyle> 34 35 containerStyle?: StyleProp<ViewStyle> 35 36 }) { ··· 68 69 }) 69 70 } 70 71 71 - let calculatedStyle: StyleProp<ViewStyle> | undefined 72 + let calculatedStyle: StyleProp<ImageStyle> | undefined 72 73 if (imgInfo && containerInfo) { 73 74 // imgInfo.height / imgInfo.width = x / containerInfo.width 74 75 // x = imgInfo.height / imgInfo.width * containerInfo.width
+12 -10
src/view/com/util/images/ImageLayoutGrid.tsx
··· 13 13 14 14 interface Dim { 15 15 width: number 16 - height: numberPressIn 16 + height: number 17 17 } 18 18 19 19 export type ImageLayoutGridType = 'two' | 'three' | 'four' ··· 28 28 type: ImageLayoutGridType 29 29 uris: string[] 30 30 onPress?: (index: number) => void 31 + onLongPress?: (index: number) => void 31 32 style?: StyleProp<ViewStyle> 32 33 }) { 33 34 const [containerInfo, setContainerInfo] = React.useState<Dim | undefined>() ··· 64 65 type: ImageLayoutGridType 65 66 uris: string[] 66 67 onPress?: (index: number) => void 68 + onLongPress?: (index: number) => void 67 69 containerInfo: Dim 68 70 }) { 69 71 const size1 = React.useMemo<ImageStyle>(() => { ··· 91 93 <TouchableOpacity 92 94 delayPressIn={DELAY_PRESS_IN} 93 95 onPress={() => onPress?.(0)} 94 - onLongPress={() => onLongPress(0)}> 96 + onLongPress={() => onLongPress?.(0)}> 95 97 <Image source={{uri: uris[0]}} style={size1} /> 96 98 </TouchableOpacity> 97 99 <View style={styles.wSpace} /> 98 100 <TouchableOpacity 99 101 delayPressIn={DELAY_PRESS_IN} 100 102 onPress={() => onPress?.(1)} 101 - onLongPress={() => onLongPress(1)}> 103 + onLongPress={() => onLongPress?.(1)}> 102 104 <Image source={{uri: uris[1]}} style={size1} /> 103 105 </TouchableOpacity> 104 106 </View> ··· 110 112 <TouchableOpacity 111 113 delayPressIn={DELAY_PRESS_IN} 112 114 onPress={() => onPress?.(0)} 113 - onLongPress={() => onLongPress(0)}> 115 + onLongPress={() => onLongPress?.(0)}> 114 116 <Image source={{uri: uris[0]}} style={size2} /> 115 117 </TouchableOpacity> 116 118 <View style={styles.wSpace} /> ··· 118 120 <TouchableOpacity 119 121 delayPressIn={DELAY_PRESS_IN} 120 122 onPress={() => onPress?.(1)} 121 - onLongPress={() => onLongPress(1)}> 123 + onLongPress={() => onLongPress?.(1)}> 122 124 <Image source={{uri: uris[1]}} style={size1} /> 123 125 </TouchableOpacity> 124 126 <View style={styles.hSpace} /> 125 127 <TouchableOpacity 126 128 delayPressIn={DELAY_PRESS_IN} 127 129 onPress={() => onPress?.(2)} 128 - onLongPress={() => onLongPress(2)}> 130 + onLongPress={() => onLongPress?.(2)}> 129 131 <Image source={{uri: uris[2]}} style={size1} /> 130 132 </TouchableOpacity> 131 133 </View> ··· 139 141 <TouchableOpacity 140 142 delayPressIn={DELAY_PRESS_IN} 141 143 onPress={() => onPress?.(0)} 142 - onLongPress={() => onLongPress(0)}> 144 + onLongPress={() => onLongPress?.(0)}> 143 145 <Image source={{uri: uris[0]}} style={size1} /> 144 146 </TouchableOpacity> 145 147 <View style={styles.hSpace} /> 146 148 <TouchableOpacity 147 149 delayPressIn={DELAY_PRESS_IN} 148 150 onPress={() => onPress?.(1)} 149 - onLongPress={() => onLongPress(1)}> 151 + onLongPress={() => onLongPress?.(1)}> 150 152 <Image source={{uri: uris[1]}} style={size1} /> 151 153 </TouchableOpacity> 152 154 </View> ··· 155 157 <TouchableOpacity 156 158 delayPressIn={DELAY_PRESS_IN} 157 159 onPress={() => onPress?.(2)} 158 - onLongPress={() => onLongPress(2)}> 160 + onLongPress={() => onLongPress?.(2)}> 159 161 <Image source={{uri: uris[2]}} style={size1} /> 160 162 </TouchableOpacity> 161 163 <View style={styles.hSpace} /> 162 164 <TouchableOpacity 163 165 delayPressIn={DELAY_PRESS_IN} 164 166 onPress={() => onPress?.(3)} 165 - onLongPress={() => onLongPress(3)}> 167 + onLongPress={() => onLongPress?.(3)}> 166 168 <Image source={{uri: uris[3]}} style={size1} /> 167 169 </TouchableOpacity> 168 170 </View>
+11 -2
src/view/lib/icons.tsx
··· 1 1 import React from 'react' 2 - import {StyleProp, ViewStyle} from 'react-native' 2 + import {StyleProp, TextStyle, ViewStyle} from 'react-native' 3 3 import Svg, {Path} from 'react-native-svg' 4 4 5 5 export function GridIcon({ ··· 428 428 size?: string | number 429 429 strokeWidth?: number 430 430 }) { 431 + let color = 'currentColor' 432 + if ( 433 + style && 434 + typeof style === 'object' && 435 + 'color' in style && 436 + typeof style.color === 'string' 437 + ) { 438 + color = style.color 439 + } 431 440 return ( 432 441 <Svg 433 442 fill="none" 434 443 viewBox="0 0 24 24" 435 444 strokeWidth={strokeWidth || 2.5} 436 - stroke={style?.color || 'currentColor'} 445 + stroke={color} 437 446 width={size || 24} 438 447 height={size || 24} 439 448 style={style}>
+1 -1
src/view/routes.ts
··· 21 21 navIdx: string 22 22 params: Record<string, any> 23 23 visible: boolean 24 - scrollElRef?: MutableRefObject<FlatList<any> | undefined> 24 + scrollElRef?: MutableRefObject<FlatList<any> | null> 25 25 } 26 26 export type Route = [React.FC<ScreenParams>, string, IconProp, RegExp] 27 27 export type MatchResult = {
+5 -14
src/view/screens/Debug.tsx
··· 5 5 import {PaletteColorName} from '../lib/ThemeContext' 6 6 import {usePalette} from '../lib/hooks/usePalette' 7 7 import {s} from '../lib/styles' 8 - import {DEF_AVATAR} from '../lib/assets' 9 8 import {displayNotification} from '../lib/notifee' 10 9 11 10 import {Text} from '../com/util/text/Text' 12 11 import {ViewSelector} from '../com/util/ViewSelector' 13 12 import {EmptyState} from '../com/util/EmptyState' 14 13 import * as LoadingPlaceholder from '../com/util/LoadingPlaceholder' 15 - import {Button} from '../com/util/forms/Button' 14 + import {Button, ButtonType} from '../com/util/forms/Button' 16 15 import {DropdownButton, DropdownItem} from '../com/util/forms/DropdownButton' 17 16 import {ToggleButton} from '../com/util/forms/ToggleButton' 18 17 import {RadioGroup} from '../com/util/forms/RadioGroup' ··· 48 47 const [currentView, setCurrentView] = React.useState<number>(0) 49 48 const pal = usePalette('default') 50 49 51 - const renderItem = (item, i) => { 50 + const renderItem = (item: any) => { 52 51 return ( 53 - <View key={`view-${i}`}> 52 + <View key={`view-${item.currentView}`}> 54 53 <View style={[s.pt10, s.pl10, s.pr10]}> 55 54 <ToggleButton 56 55 type="default-light" ··· 179 178 "Hello world! This is a test of the notifications card. The text is long to see how that's handled.", 180 179 ) 181 180 } 182 - const triggerImg = () => { 183 - displayNotification( 184 - 'Paul Frazee liked your post', 185 - "Hello world! This is a test of the notifications card. The text is long to see how that's handled.", 186 - DEF_AVATAR, 187 - ) 188 - } 189 181 return ( 190 182 <View style={s.p10}> 191 183 <View style={s.flexRow}> 192 184 <Button onPress={trigger} label="Trigger" /> 193 - <Button onPress={triggerImg} label="Trigger w/image" style={s.ml5} /> 194 185 </View> 195 186 </View> 196 187 ) ··· 484 475 ] 485 476 function RadioButtonsView() { 486 477 const defaultPal = usePalette('default') 487 - const [rgType, setRgType] = React.useState('default-light') 478 + const [rgType, setRgType] = React.useState<ButtonType>('default-light') 488 479 return ( 489 480 <View style={[defaultPal.view]}> 490 481 <RadioGroup 491 482 type={rgType} 492 483 items={RADIO_BUTTON_ITEMS} 493 484 initialSelection="default-light" 494 - onSelect={setRgType} 485 + onSelect={v => setRgType(v as ButtonType)} 495 486 /> 496 487 </View> 497 488 )
+51 -49
src/view/shell/desktop-web/left-column.tsx
··· 1 1 import React from 'react' 2 - import {Pressable, View, StyleSheet} from 'react-native' 2 + import {View} from 'react-native' 3 3 4 - export const NavItem: React.FC<{label: string; screen: string}> = ({ 5 - label, 6 - screen, 7 - }) => { 8 - const Link = <></> // TODO 9 - return ( 10 - <View> 11 - <Pressable 12 - style={state => [ 13 - // @ts-ignore it does exist! (react-native-web) -prf 14 - state.hovered && styles.navItemHovered, 15 - ]}> 16 - <Link 17 - style={[ 18 - styles.navItemLink, 19 - false /* TODO route.name === screen*/ && styles.navItemLinkSelected, 20 - ]} 21 - to={{screen, params: {}}}> 22 - {label} 23 - </Link> 24 - </Pressable> 25 - </View> 26 - ) 27 - } 4 + // export const NavItem: React.FC<{label: string; screen: string}> = ({ 5 + // label, 6 + // screen, 7 + // }) => { 8 + // const Link = <></> // TODO 9 + // return ( 10 + // <View> 11 + // <Pressable 12 + // style={state => [ 13 + // // @ts-ignore it does exist! (react-native-web) -prf 14 + // state.hovered && styles.navItemHovered, 15 + // ]}> 16 + // <Link 17 + // style={[ 18 + // styles.navItemLink, 19 + // false /* TODO route.name === screen*/ && styles.navItemLinkSelected, 20 + // ]} 21 + // to={{screen, params: {}}}> 22 + // {label} 23 + // </Link> 24 + // </Pressable> 25 + // </View> 26 + // ) 27 + // } 28 28 29 29 export const DesktopLeftColumn: React.FC = () => { 30 - return ( 31 - <View style={styles.container}> 32 - <NavItem screen="Home" label="Home" /> 33 - <NavItem screen="Search" label="Search" /> 34 - <NavItem screen="Notifications" label="Notifications" /> 35 - </View> 36 - ) 30 + // TODO 31 + return <View /> 32 + // return ( 33 + // <View style={styles.container}> 34 + // <NavItem screen="Home" label="Home" /> 35 + // <NavItem screen="Search" label="Search" /> 36 + // <NavItem screen="Notifications" label="Notifications" /> 37 + // </View> 38 + // ) 37 39 } 38 40 39 - const styles = StyleSheet.create({ 40 - container: { 41 - position: 'absolute', 42 - left: 'calc(50vw - 500px)', 43 - width: '200px', 44 - height: '100%', 45 - }, 46 - navItemHovered: { 47 - backgroundColor: 'gray', 48 - }, 49 - navItemLink: { 50 - padding: '1rem', 51 - }, 52 - navItemLinkSelected: { 53 - color: 'blue', 54 - }, 55 - }) 41 + // const styles = StyleSheet.create({ 42 + // container: { 43 + // position: 'absolute', 44 + // left: 'calc(50vw - 500px)', 45 + // width: '200px', 46 + // height: '100%', 47 + // }, 48 + // navItemHovered: { 49 + // backgroundColor: 'gray', 50 + // }, 51 + // navItemLink: { 52 + // padding: '1rem', 53 + // }, 54 + // navItemLinkSelected: { 55 + // color: 'blue', 56 + // }, 57 + // })
+7 -4
src/view/shell/mobile/TabsSelector.tsx
··· 36 36 undefined, 37 37 ) 38 38 const closeInterp = useAnimatedValue(0) 39 + const tabsContainerRef = useRef<View>(null) 39 40 const tabsRef = useRef<ScrollView>(null) 40 41 const tabRefs = useMemo( 41 42 () => 42 43 Array.from({length: store.nav.tabs.length}).map(() => 43 - createRef<Animated.View>(), 44 + createRef<View>(), 44 45 ), 45 46 [store.nav.tabs.length], 46 47 ) ··· 90 91 const onLayout = () => { 91 92 // focus the current tab 92 93 const targetTab = tabRefs[store.nav.tabIndex] 93 - if (tabsRef.current && targetTab.current) { 94 + if (tabsContainerRef.current && tabsRef.current && targetTab.current) { 94 95 targetTab.current.measureLayout?.( 95 - tabsRef.current, 96 + tabsContainerRef.current, 96 97 (_left: number, top: number) => { 97 98 tabsRef.current?.scrollTo({y: top, animated: false}) 98 99 }, ··· 162 163 </TouchableWithoutFeedback> 163 164 </View> 164 165 </View> 165 - <View style={[s.p10, styles.section, styles.sectionGrayBg]}> 166 + <View 167 + ref={tabsContainerRef} 168 + style={[s.p10, styles.section, styles.sectionGrayBg]}> 166 169 <ScrollView ref={tabsRef} style={styles.tabs}> 167 170 {store.nav.tabs.map((tab, tabIndex) => { 168 171 const {icon} = match(tab.current.url)
+31 -29
src/view/shell/mobile/index.tsx
··· 67 67 onLongPress?: (event: GestureResponderEvent) => void 68 68 }) => { 69 69 const pal = usePalette('default') 70 - let size = 24 71 - let addedStyles 72 - let IconEl 70 + let iconEl 73 71 if (icon === 'menu') { 74 - IconEl = GridIcon 72 + iconEl = <GridIcon style={[styles.ctrlIcon, pal.text]} /> 75 73 } else if (icon === 'menu-solid') { 76 - IconEl = GridIconSolid 74 + iconEl = <GridIconSolid style={[styles.ctrlIcon, pal.text]} /> 77 75 } else if (icon === 'home') { 78 - IconEl = HomeIcon 79 - size = 27 76 + iconEl = <HomeIcon size={27} style={[styles.ctrlIcon, pal.text]} /> 80 77 } else if (icon === 'home-solid') { 81 - IconEl = HomeIconSolid 82 - size = 27 78 + iconEl = <HomeIconSolid size={27} style={[styles.ctrlIcon, pal.text]} /> 83 79 } else if (icon === 'bell') { 84 - IconEl = BellIcon 85 - size = 27 86 - addedStyles = {position: 'relative', top: -1} as ViewStyle 80 + const addedStyles = {position: 'relative', top: -1} as ViewStyle 81 + iconEl = ( 82 + <BellIcon size={27} style={[styles.ctrlIcon, pal.text, addedStyles]} /> 83 + ) 87 84 } else if (icon === 'bell-solid') { 88 - IconEl = BellIconSolid 89 - size = 27 90 - addedStyles = {position: 'relative', top: -1} as ViewStyle 85 + const addedStyles = {position: 'relative', top: -1} as ViewStyle 86 + iconEl = ( 87 + <BellIconSolid 88 + size={27} 89 + style={[styles.ctrlIcon, pal.text, addedStyles]} 90 + /> 91 + ) 91 92 } else { 92 - IconEl = FontAwesomeIcon 93 + iconEl = ( 94 + <FontAwesomeIcon 95 + size={24} 96 + icon={icon} 97 + style={[styles.ctrlIcon, pal.text]} 98 + /> 99 + ) 93 100 } 94 101 95 102 return ( ··· 108 115 <Text style={styles.tabCountLabel}>{tabCount}</Text> 109 116 </View> 110 117 ) : undefined} 111 - <IconEl 112 - size={size} 113 - style={[styles.ctrlIcon, pal.text, addedStyles]} 114 - icon={icon} 115 - /> 118 + {iconEl} 116 119 </TouchableOpacity> 117 120 ) 118 121 } ··· 122 125 const pal = usePalette('default') 123 126 const store = useStores() 124 127 const [isTabsSelectorActive, setTabsSelectorActive] = useState(false) 125 - const scrollElRef = useRef<FlatList | undefined>() 128 + const scrollElRef = useRef<FlatList>(null) 126 129 const winDim = useWindowDimensions() 127 130 const [menuSwipingDirection, setMenuSwipingDirection] = useState(0) 128 131 const swipeGestureInterp = useAnimatedValue(0) ··· 292 295 ) 293 296 shouldRenderMenu = true 294 297 } 295 - const menuSwipeTransform = { 296 - transform: [{translateX: menuTranslateX}], 297 - } 298 + const menuSwipeTransform = menuTranslateX 299 + ? { 300 + transform: [{translateX: menuTranslateX}], 301 + } 302 + : undefined 298 303 const swipeOpacity = { 299 304 opacity: swipeGestureInterp.interpolate({ 300 305 inputRange: [-1, 0, 1], ··· 417 422 ) : undefined} 418 423 {shouldRenderMenu && ( 419 424 <Animated.View style={[styles.menuDrawer, menuSwipeTransform]}> 420 - <Menu 421 - visible={isMenuActive} 422 - onClose={() => store.shell.setMainMenuOpen(false)} 423 - /> 425 + <Menu onClose={() => store.shell.setMainMenuOpen(false)} /> 424 426 </Animated.View> 425 427 )} 426 428 </HorzSwipe>