Fix all type errors

zio/stable
Paul Frazee 2023-01-26 11:25:52 -06:00
parent c4ba5e7fd5
commit 7e3f6f0306
45 changed files with 377 additions and 294 deletions

View File

@ -6,7 +6,7 @@ const BottomSheetModalContext = React.createContext(null)
const BottomSheetModalProvider = (props: any) => {
return <BottomSheetModalContext.Provider {...props} value={{}} />
}
class BottomSheet extends React.Component {
class BottomSheet extends React.Component<{onClose?: () => void}> {
snapToIndex() {}
snapToPosition() {}
expand() {}

View File

@ -9,11 +9,9 @@ import {MeModel} from '../src/state/models/me'
import {OnboardModel} from '../src/state/models/onboard'
import {ProfilesViewModel} from '../src/state/models/profiles-view'
import {LinkMetasViewModel} from '../src/state/models/link-metas-view'
import {MembershipsViewModel} from '../src/state/models/memberships-view'
import {FeedModel} from '../src/state/models/feed-view'
import {NotificationsViewModel} from '../src/state/models/notifications-view'
import {ProfileViewModel} from '../src/state/models/profile-view'
import {MembersViewModel} from '../src/state/models/members-view'
import {ProfileUiModel, Sections} from '../src/state/models/profile-ui'
import {SessionServiceClient} from '@atproto/api'
import {UserAutocompleteViewModel} from '../src/state/models/user-autocomplete-view'
@ -70,95 +68,13 @@ export const mockedProfileStore = {
// unknown required because of the missing private methods: _xLoading, _xIdle, _load, _replaceAll
} as unknown as ProfileViewModel
export const mockedMembersStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
actor: 'test actor',
},
subject: {
did: 'test did',
handle: '',
displayName: '',
declaration: {
cid: '',
actorType: '',
},
avatar: undefined,
},
members: [
{
did: 'test did2',
declaration: {
cid: '',
actorType: '',
},
handle: 'testhandle',
displayName: 'test name',
indexedAt: '',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
isMember: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
removeMember: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append
} as unknown as MembersViewModel
export const mockedMembershipsStore = {
isLoading: false,
isRefreshing: false,
hasLoaded: true,
error: '',
params: {
actor: '',
limit: 1,
before: '',
},
subject: {
did: 'test did',
handle: '',
displayName: '',
declaration: {cid: '', actorType: ''},
avatar: undefined,
},
memberships: [
{
did: 'test did',
declaration: {
cid: '',
actorType: 'app.bsky.system.actorUser',
},
handle: ',',
displayName: '',
createdAt: '',
indexedAt: '',
_reactKey: 'item-1',
},
],
rootStore: {} as RootStoreModel,
hasContent: true,
hasError: false,
isEmpty: false,
isMemberOf: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),
refresh: jest.fn().mockResolvedValue({}),
loadMore: jest.fn(),
// unknown required because of the missing private methods: _xLoading, _xIdle, _fetch, _replaceAll, _append
} as unknown as MembershipsViewModel
export const mockedFeedItemStore = {
_reactKey: 'item-1',
_isThreadParent: false,
_isThreadChildElided: false,
_isThreadChild: false,
_hideParent: false,
_isRenderingAsThread: false,
post: {
uri: 'testuri',
cid: 'test cid',
@ -475,13 +391,13 @@ export const mockedSessionStore = {
export const mockedNavigationTabStore = {
serialize: jest.fn(),
hydrate: jest.fn(),
id: 0,
id: '0',
history: [
{
url: '',
ts: 0,
title: '',
id: 0,
id: '0',
},
],
index: 0,
@ -490,7 +406,7 @@ export const mockedNavigationTabStore = {
url: '',
ts: 0,
title: '',
id: 0,
id: '0',
},
canGoBack: false,
canGoForward: false,
@ -499,7 +415,7 @@ export const mockedNavigationTabStore = {
url: '',
title: '',
index: 0,
id: 0,
id: '0',
},
],
forwardTen: [
@ -507,7 +423,7 @@ export const mockedNavigationTabStore = {
url: '',
title: '',
index: 0,
id: 0,
id: '0',
},
],
navigate: jest.fn(),
@ -524,7 +440,7 @@ export const mockedNavigationTabStore = {
url: '/',
title: '',
index: 1,
id: 1,
id: '1',
},
],
getForwardList: jest.fn(),
@ -582,13 +498,13 @@ export const mockedMeStore = {
avatar: '',
notificationCount: 0,
rootStore: {} as RootStoreModel,
memberships: mockedMembershipsStore,
mainFeed: mockedFeedStore,
notifications: mockedNotificationsStore,
clear: jest.fn(),
load: jest.fn(),
clearNotificationCount: jest.fn(),
fetchNotifications: jest.fn(),
bgFetchNotifications: jest.fn(),
refreshMemberships: jest.fn(),
} as MeModel
@ -650,6 +566,11 @@ export const mockedRootStore = {
hydrate: jest.fn(),
fetchStateUpdate: jest.fn(),
clearAll: jest.fn(),
onPostDeleted: jest.fn(),
emitPostDeleted: jest.fn(),
initBgFetch: jest.fn(),
onBgFetch: jest.fn(),
onBgFetchTimeout: jest.fn(),
session: mockedSessionStore,
nav: mockedNavigationStore,
shell: mockedShellStore,
@ -663,8 +584,6 @@ export const mockedRootStore = {
export const mockedProfileUiStore = {
profile: mockedProfileStore,
feed: mockedFeedStore,
memberships: mockedMembershipsStore,
members: mockedMembersStore,
selectedViewIndex: 0,
rootStore: mockedRootStore,
params: {
@ -675,7 +594,7 @@ export const mockedProfileUiStore = {
isRefreshing: false,
isUser: true,
isScene: false,
selectorItems: [Sections.Posts, Sections.PostsWithReplies, Sections.Scenes],
selectorItems: [Sections.Posts, Sections.PostsWithReplies],
selectedView: Sections.Posts,
setSelectedViewIndex: jest.fn(),
setup: jest.fn().mockResolvedValue({aborted: false}),

View File

@ -41,6 +41,7 @@ describe('extractHtmlMeta', () => {
it.each(cases)(
'given the html tag %p, returns %p',
// @ts-ignore not worth fixing -prf
(input, expectedResult) => {
const output = extractHtmlMeta({html: input as string, hostname: ''})
expect(output).toEqual(expectedResult)
@ -86,6 +87,7 @@ describe('extractHtmlMeta', () => {
title: '@bluesky on Twitter',
}
const output = extractHtmlMeta({
html: '',
hostname: 'twitter.com',
pathname: '/bluesky',
})
@ -97,6 +99,7 @@ describe('extractHtmlMeta', () => {
title: 'Tweet by @bluesky',
}
const output = extractHtmlMeta({
html: '',
hostname: 'twitter.com',
pathname: '/bluesky/status/1582437529969917953',
})
@ -108,6 +111,7 @@ describe('extractHtmlMeta', () => {
title: 'Twitter',
}
const output = extractHtmlMeta({
html: '',
hostname: 'twitter.com',
pathname: '/i/articles/follows/-1675653703?time_window=24',
})

View File

@ -5,7 +5,7 @@ import {mockedRootStore, mockedShellStore} from '../../../__mocks__/state-mock'
describe('useOnMainScroll', () => {
const mockedProps = {
navIdx: [0, 0] as [number, number],
navIdx: '0-0',
params: {},
visible: true,
}

View File

@ -5,7 +5,7 @@ import {cleanup, fireEvent, render} from '../../../jest/test-utils'
describe('Search', () => {
jest.useFakeTimers()
const mockedProps = {
navIdx: [0, 0] as [number, number],
navIdx: '0-0',
params: {
name: 'test name',
},

View File

@ -179,7 +179,7 @@ async function genMockData(pdsUrl: string): Promise<TestUsers> {
did: subject.did,
declarationCid: subject.declarationCid,
},
createdAt: date.next().value,
createdAt: date.next().value || '',
},
)
}

View File

@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {DesktopWebShell} from './view/shell/desktop-web'
import Toast from 'react-native-root-toast'
// import Toast from 'react-native-root-toast' TODO
function App() {
const [rootStore, setRootStore] = useState<RootStoreModel | undefined>(
@ -23,9 +23,9 @@ function App() {
return (
<RootStoreProvider value={rootStore}>
<DesktopWebShell />
<Toast.ToastContainer />
</RootStoreProvider>
)
// <Toast.ToastContainer /> TODO
}
export default App

View File

@ -63,7 +63,7 @@ export const extractHtmlMeta = ({
// Workaround for some websites not having a title or description in the meta tags in the initial serve
if (isYoutubeUrl) {
res = {...res, ...extractYoutubeMeta(html)}
} else if (isTwitterUrl) {
} else if (isTwitterUrl && pathname) {
res = {...extractTwitterMeta({pathname})}
}

View File

@ -98,8 +98,6 @@ export class ProfileUiModel {
const view = this.currentView
if (view instanceof FeedModel) {
await view.update()
} else {
await view.refresh()
}
}

View File

@ -16,7 +16,10 @@ import PasteInput, {
PasteInputRef,
} from '@mattermost/react-native-paste-input'
import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {useAnalytics} from '@segment/analytics-react-native'
import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view'
import {Autocomplete} from './Autocomplete'
@ -438,7 +441,11 @@ export const ComposePost = observer(function ComposePost({
hitSlop={HITSLOP}>
<FontAwesomeIcon
icon={['far', 'image']}
style={selectedPhotos.length < 4 ? pal.link : pal.textLight}
style={
(selectedPhotos.length < 4
? pal.link
: pal.textLight) as FontAwesomeIconStyle
}
size={24}
/>
</TouchableOpacity>

View File

@ -1,6 +1,9 @@
import React, {useCallback} from 'react'
import {Image, StyleSheet, TouchableOpacity, ScrollView} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {
openPicker,
openCamera,
@ -131,13 +134,21 @@ export const PhotoCarouselPicker = ({
testID="openCameraButton"
style={[styles.galleryButton, pal.border, styles.photo]}
onPress={handleOpenCamera}>
<FontAwesomeIcon icon="camera" size={24} style={pal.link} />
<FontAwesomeIcon
icon="camera"
size={24}
style={pal.link as FontAwesomeIconStyle}
/>
</TouchableOpacity>
<TouchableOpacity
testID="openGalleryButton"
style={[styles.galleryButton, pal.border, styles.photo]}
onPress={handleOpenGallery}>
<FontAwesomeIcon icon="image" style={pal.link} size={24} />
<FontAwesomeIcon
icon="image"
style={pal.link as FontAwesomeIconStyle}
size={24}
/>
</TouchableOpacity>
{localPhotos.photos.map((item: PhotoIdentifier, index: number) => (
<TouchableOpacity

View File

@ -7,7 +7,10 @@ import {
View,
} from 'react-native'
import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {observer} from 'mobx-react-lite'
import _omit from 'lodash.omit'
import {ErrorScreen} from '../util/error/ErrorScreen'
@ -199,7 +202,7 @@ const User = ({
style={[styles.btn, styles.gradientBtn]}>
<FontAwesomeIcon
icon="plus"
style={[s.white, s.mr5]}
style={[s.white as FontAwesomeIconStyle, s.mr5]}
size={15}
/>
<Text style={[s.white, s.fw600, s.f15]}>Follow</Text>

View File

@ -0,0 +1,21 @@
// default implementation fallback for web
import React from 'react'
import {View} from 'react-native'
import {ImageSource} from '../../@types'
type Props = {
imageSrc: ImageSource
onRequestClose: () => void
onZoom: (scaled: boolean) => void
onLongPress: (image: ImageSource) => void
delayLongPress: number
swipeToCloseEnabled?: boolean
doubleTapToZoomEnabled?: boolean
}
const ImageItem = (_props: Props) => {
return <View />
}
export default React.memo(ImageItem)

View File

@ -47,8 +47,8 @@ const useImageDimensions = (image: ImageSource): Dimensions | null => {
if (imageDimensions) {
resolve(imageDimensions)
} else {
// @ts-ignore
Image.getSizeWithHeaders(
// @ts-ignore
source.uri,
source.headers,
(width: number, height: number) => {

View File

@ -61,7 +61,7 @@ const usePanResponder = ({
let tmpTranslate: Position | null = null
let isDoubleTapPerformed = false
let lastTapTS: number | null = null
let longPressHandlerRef: number | null = null
let longPressHandlerRef: NodeJS.Timeout | null = null
const meaningfulShift = MIN_DIMENSION * 0.01
const scaleValue = new Animated.Value(initialScale)

View File

@ -77,6 +77,7 @@ export const getImageStyles = (
const transform = translate.getTranslateTransform()
if (scale) {
// @ts-ignore TODO - is scale incorrect? might need to remove -prf
transform.push({scale}, {perspective: new Animated.Value(1000)})
}

View File

@ -5,6 +5,7 @@ import ImageView from './ImageViewing'
import {useStores} from '../../../state'
import * as models from '../../../state/models/shell-ui'
import {saveImageModal} from '../../../lib/images'
import {ImageSource} from './ImageViewing/@types'
export const Lightbox = observer(function Lightbox() {
const store = useStores()
@ -15,8 +16,14 @@ export const Lightbox = observer(function Lightbox() {
const onClose = () => {
store.shell.closeLightbox()
}
const onLongPress = ({uri}: {uri: string}) => {
saveImageModal({uri})
const onLongPress = (image: ImageSource) => {
if (
typeof image === 'object' &&
'uri' in image &&
typeof image.uri === 'string'
) {
saveImageModal({uri: image.uri})
}
}
if (store.shell.activeLightbox?.name === 'profile-image') {

View File

@ -9,7 +9,10 @@ import {
TouchableOpacity,
View,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {ComAtprotoAccountCreate} from '@atproto/api'
import * as EmailValidator from 'email-validator'
import {useAnalytics} from '@segment/analytics-react-native'
@ -264,7 +267,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
<Picker
style={[pal.text, styles.picker]}
labelStyle={styles.pickerLabel}
iconStyle={pal.textLight}
iconStyle={pal.textLight as FontAwesomeIconStyle}
value={userDomain}
items={serviceDescription.availableUserDomains.map(d => ({
label: `.${d}`,
@ -371,7 +374,11 @@ const Policies = ({
return (
<View style={styles.policies}>
<View style={[styles.errorIcon, {borderColor: pal.colors.text}, s.mt2]}>
<FontAwesomeIcon icon="exclamation" style={pal.textLight} size={10} />
<FontAwesomeIcon
icon="exclamation"
style={pal.textLight as FontAwesomeIconStyle}
size={10}
/>
</View>
<Text style={[pal.textLight, s.pl5, s.flex1]}>
This service has not provided terms of service or a privacy policy.

View File

@ -8,7 +8,10 @@ import {
TouchableOpacity,
View,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import * as EmailValidator from 'email-validator'
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
import {useAnalytics} from '@segment/analytics-react-native'
@ -337,7 +340,11 @@ const LoginForm = ({
{toNiceDomain(serviceUrl)}
</Text>
<View style={[pal.btn, styles.textBtnFakeInnerBtn]}>
<FontAwesomeIcon icon="pen" size={12} style={pal.textLight} />
<FontAwesomeIcon
icon="pen"
size={12}
style={pal.textLight as FontAwesomeIconStyle}
/>
</View>
</TouchableOpacity>
</View>
@ -514,7 +521,11 @@ const ForgotPasswordForm = ({
{toNiceDomain(serviceUrl)}
</Text>
<View style={[pal.btn, styles.textBtnFakeInnerBtn]}>
<FontAwesomeIcon icon="pen" size={12} style={pal.text} />
<FontAwesomeIcon
icon="pen"
size={12}
style={pal.text as FontAwesomeIconStyle}
/>
</View>
</TouchableOpacity>
<View style={[pal.borderDark, styles.groupContent]}>

View File

@ -108,7 +108,6 @@ export function Component({
<UserBanner
banner={userBanner}
onSelectNewBanner={onSelectNewBanner}
handle={profileView.handle}
/>
<View style={styles.avi}>
<UserAvatar

View File

@ -62,18 +62,10 @@ export const Modal = observer(function Modal() {
)
} else if (store.shell.activeModal?.name === 'report-post') {
snapPoints = ReportPostModal.snapPoints
element = (
<ReportPostModal.Component
{...(store.shell.activeModal as models.ReportPostModal)}
/>
)
element = <ReportPostModal.Component />
} else if (store.shell.activeModal?.name === 'report-account') {
snapPoints = ReportAccountModal.snapPoints
element = (
<ReportAccountModal.Component
{...(store.shell.activeModal as models.ReportAccountModal)}
/>
)
element = <ReportAccountModal.Component />
} else {
element = <View />
}

View File

@ -1,6 +1,9 @@
import React, {useState} from 'react'
import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
@ -37,13 +40,19 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
style={styles.btn}
onPress={() => doSelect(LOCAL_DEV_SERVICE)}>
<Text style={styles.btnText}>Local dev server</Text>
<FontAwesomeIcon icon="arrow-right" style={s.white} />
<FontAwesomeIcon
icon="arrow-right"
style={s.white as FontAwesomeIconStyle}
/>
</TouchableOpacity>
<TouchableOpacity
style={styles.btn}
onPress={() => doSelect(STAGING_SERVICE)}>
<Text style={styles.btnText}>Staging</Text>
<FontAwesomeIcon icon="arrow-right" style={s.white} />
<FontAwesomeIcon
icon="arrow-right"
style={s.white as FontAwesomeIconStyle}
/>
</TouchableOpacity>
</>
) : undefined}
@ -51,7 +60,10 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
style={styles.btn}
onPress={() => doSelect(PROD_SERVICE)}>
<Text style={styles.btnText}>Bluesky.Social</Text>
<FontAwesomeIcon icon="arrow-right" style={s.white} />
<FontAwesomeIcon
icon="arrow-right"
style={s.white as FontAwesomeIconStyle}
/>
</TouchableOpacity>
</View>
<View style={styles.group}>
@ -74,7 +86,7 @@ export function Component({onSelect}: {onSelect: (url: string) => void}) {
onPress={() => doSelect(customUrl)}>
<FontAwesomeIcon
icon="check"
style={[s.black, styles.checkIcon]}
style={[s.black as FontAwesomeIconStyle, styles.checkIcon]}
size={18}
/>
</TouchableOpacity>

View File

@ -9,7 +9,11 @@ import {
} from 'react-native'
import {AppBskyEmbedImages} from '@atproto/api'
import {AtUri} from '../../../third-party/uri'
import {FontAwesomeIcon, Props} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
Props,
} from '@fortawesome/react-native-fontawesome'
import {NotificationsViewItemModel} from '../../../state/models/notifications-view'
import {PostThreadViewModel} from '../../../state/models/post-thread-view'
import {s, colors} from '../../lib/styles'
@ -98,18 +102,21 @@ export const FeedItem = observer(function FeedItem({
if (item.isUpvote) {
action = 'liked your post'
icon = 'HeartIconSolid'
iconStyle = [s.red3, {position: 'relative', top: -4}]
iconStyle = [
s.red3 as FontAwesomeIconStyle,
{position: 'relative', top: -4},
]
} else if (item.isRepost) {
action = 'reposted your post'
icon = 'retweet'
iconStyle = [s.green3]
iconStyle = [s.green3 as FontAwesomeIconStyle]
} else if (item.isReply) {
action = 'replied to your post'
icon = ['far', 'comment']
} else if (item.isFollow) {
action = 'followed you'
icon = 'user-plus'
iconStyle = [s.blue3]
iconStyle = [s.blue3 as FontAwesomeIconStyle]
} else {
return <></>
}
@ -292,7 +299,6 @@ function ExpandedAuthorsList({
authors.length * (EXPANDED_AUTHOR_EL_HEIGHT + 10) /*10=margin*/
const heightStyle = {
height: Animated.multiply(heightInterp, targetHeight),
overflow: 'hidden',
}
React.useEffect(() => {
Animated.timing(heightInterp, {
@ -302,7 +308,12 @@ function ExpandedAuthorsList({
}).start()
}, [heightInterp, visible])
return (
<Animated.View style={[heightStyle, visible ? s.mb10 : undefined]}>
<Animated.View
style={[
heightStyle,
styles.overflowHidden,
visible ? s.mb10 : undefined,
]}>
{authors.map(author => (
<Link
key={author.href}
@ -360,6 +371,10 @@ function AdditionalPostText({
}
const styles = StyleSheet.create({
overflowHidden: {
overflow: 'hidden',
},
outer: {
padding: 10,
paddingRight: 15,

View File

@ -9,13 +9,23 @@ import {
View,
} from 'react-native'
import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
import {s} from '../../lib/styles'
import {TABS_EXPLAINER} from '../../lib/assets'
import {TABS_ENABLED} from '../../../build-flags'
const ROUTES = TABS_ENABLED
? [
{key: 'intro', title: 'Intro'},
{key: 'tabs', title: 'Tabs'},
]
: [{key: 'intro', title: 'Intro'}]
const Intro = () => (
<View style={styles.explainer}>
<Text
@ -37,7 +47,7 @@ const Tabs = () => (
<View style={s.flex1} />
<FontAwesomeIcon
icon={['far', 'clone']}
style={[s.black, s.mb5]}
style={[s.black as FontAwesomeIconStyle, s.mb5]}
size={36}
/>
<View style={s.flex1} />
@ -62,14 +72,10 @@ export const FeatureExplainer = () => {
const layout = useWindowDimensions()
const store = useStores()
const [index, setIndex] = useState(0)
const routes = [
{key: 'intro', title: 'Intro'},
TABS_ENABLED ? {key: 'tabs', title: 'Tabs'} : undefined,
].filter(Boolean)
const onPressSkip = () => store.onboard.next()
const onPressNext = () => {
if (index >= routes.length - 1) {
if (index >= ROUTES.length - 1) {
store.onboard.next()
} else {
setIndex(index + 1)
@ -103,12 +109,12 @@ export const FeatureExplainer = () => {
)
}
const FirstExplainer = SCENE_MAP[routes[0]?.key as keyof typeof SCENE_MAP]
const FirstExplainer = SCENE_MAP[ROUTES[0]?.key as keyof typeof SCENE_MAP]
return (
<SafeAreaView style={styles.container}>
{routes.length > 1 ? (
{ROUTES.length > 1 ? (
<TabView
navigationState={{index, routes}}
navigationState={{index, routes: ROUTES}}
renderScene={renderScene}
renderTabBar={renderTabBar}
onIndexChange={setIndex}

View File

@ -3,7 +3,10 @@ import {observer} from 'mobx-react-lite'
import {StyleSheet, View} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import {AtUri} from '../../../third-party/uri'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
import {Link} from '../util/Link'
import {RichText} from '../util/text/RichText'
@ -59,7 +62,7 @@ export const PostThreadItem = observer(function PostThreadItem({
replyTo: {
uri: item.post.uri,
cid: item.post.cid,
text: record.text as string,
text: record?.text as string,
author: {
handle: item.post.author.handle,
displayName: item.post.author.displayName,
@ -103,7 +106,10 @@ export const PostThreadItem = observer(function PostThreadItem({
if (deleted) {
return (
<View style={[styles.outer, pal.border, pal.view, s.p20, s.flexRow]}>
<FontAwesomeIcon icon={['far', 'trash-can']} style={pal.icon} />
<FontAwesomeIcon
icon={['far', 'trash-can']}
style={pal.icon as FontAwesomeIconStyle}
/>
<Text style={[pal.textLight, s.ml10]}>This post has been deleted.</Text>
</View>
)

View File

@ -1,7 +1,7 @@
import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite'
import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
import {VotesViewModel, VotesItem} from '../../../state/models/votes-view'
import {VotesViewModel, VoteItem} from '../../../state/models/votes-view'
import {Link} from '../util/Link'
import {Text} from '../util/text/Text'
import {ErrorMessage} from '../util/error/ErrorMessage'
@ -56,7 +56,7 @@ export const PostVotedBy = observer(function PostVotedBy({
// loaded
// =
const renderItem = ({item}: {item: VotesItem}) => <LikedByItem item={item} />
const renderItem = ({item}: {item: VoteItem}) => <LikedByItem item={item} />
return (
<FlatList
data={view.votes}
@ -76,7 +76,7 @@ export const PostVotedBy = observer(function PostVotedBy({
)
})
const LikedByItem = ({item}: {item: VotesItem}) => {
const LikedByItem = ({item}: {item: VoteItem}) => {
const pal = usePalette('default')
return (

View File

@ -1,6 +1,6 @@
import React, {useState, useEffect} from 'react'
import {observer} from 'mobx-react-lite'
import {StyleSheet, View} from 'react-native'
import {StyleProp, StyleSheet, TextStyle, View} from 'react-native'
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
import {ErrorMessage} from '../util/error/ErrorMessage'
import {Text} from '../util/text/Text'
@ -12,7 +12,7 @@ export const PostText = observer(function PostText({
style,
}: {
uri: string
style?: StyleProp
style?: StyleProp<TextStyle>
}) {
const store = useStores()
const [model, setModel] = useState<PostModel | undefined>()

View File

@ -4,7 +4,10 @@ import {StyleSheet, View} from 'react-native'
import Clipboard from '@react-native-clipboard/clipboard'
import Svg, {Circle, Line} from 'react-native-svg'
import {AtUri} from '../../../third-party/uri'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {FeedItemModel} from '../../../state/models/feed-view'
import {Link} from '../util/Link'
import {Text} from '../util/text/Text'
@ -137,7 +140,10 @@ export const FeedItem = observer(function ({
}>
<FontAwesomeIcon
icon="retweet"
style={[styles.includeReasonIcon, {color: pal.colors.textLight}]}
style={[
styles.includeReasonIcon,
{color: pal.colors.textLight} as FontAwesomeIconStyle,
]}
/>
<Text type="sm-bold" style={pal.textLight}>
Reposted by{' '}
@ -167,7 +173,10 @@ export const FeedItem = observer(function ({
<FontAwesomeIcon
icon="reply"
size={9}
style={[{color: pal.colors.textLight}, s.mr5]}
style={[
{color: pal.colors.textLight} as FontAwesomeIconStyle,
s.mr5,
]}
/>
<Text type="md" style={[pal.textLight, s.mr2]}>
Reply to

View File

@ -97,7 +97,11 @@ const User = ({item}: {item: FollowItem}) => {
size={40}
displayName={item.displayName}
handle={item.handle}
avatar={item.avatar}
avatar={
item.avatar as
| string
| undefined /* HACK: type signature is wrong in the api */
}
/>
</View>
<View style={styles.layoutContent}>

View File

@ -8,7 +8,10 @@ import {
View,
} from 'react-native'
import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {BlurView} from '@react-native-community/blur'
import {ProfileViewModel} from '../../../state/models/profile-view'
import {useStores} from '../../../state'
@ -142,7 +145,7 @@ export const ProfileHeader = observer(function ProfileHeader({
}
return (
<View style={pal.view}>
<UserBanner handle={view.handle} banner={view.banner} />
<UserBanner banner={view.banner} />
<View style={styles.content}>
<View style={[styles.buttonsLine]}>
{isMe ? (
@ -181,7 +184,10 @@ export const ProfileHeader = observer(function ProfileHeader({
start={{x: 0, y: 0}}
end={{x: 1, y: 1}}
style={[styles.btn, styles.gradientBtn]}>
<FontAwesomeIcon icon="plus" style={[s.white, s.mr5]} />
<FontAwesomeIcon
icon="plus"
style={[s.white as FontAwesomeIconStyle, s.mr5]}
/>
<Text type="button" style={[s.white, s.bold]}>
Follow
</Text>

View File

@ -1,7 +1,10 @@
import React from 'react'
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {IconProp} from '@fortawesome/fontawesome-svg-core'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {Text} from './text/Text'
import {UserGroupIcon} from '../../lib/icons'
import {usePalette} from '../../lib/hooks/usePalette'
@ -25,7 +28,10 @@ export function EmptyState({
<FontAwesomeIcon
icon={icon}
size={64}
style={[styles.icon, {color: pal.colors.emptyStateIcon}]}
style={[
styles.icon,
{color: pal.colors.emptyStateIcon} as FontAwesomeIconStyle,
]}
/>
)}
</View>

View File

@ -63,7 +63,7 @@ export function PostLoadingPlaceholder({
</View>
<View style={s.flex1}>
<HeartIcon
style={{color: theme.palette.default.icon}}
style={{color: theme.palette.default.icon} as ViewStyle}
size={17}
strokeWidth={1.7}
/>

View File

@ -7,7 +7,10 @@ import {
View,
ViewStyle,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'
import {Text} from './text/Text'
import {PostDropdownBtn} from './forms/DropdownButton'
@ -147,7 +150,9 @@ export function PostCtrls(opts: PostCtrlsOpts) {
<Animated.View style={anim1Style}>
<RepostIcon
style={
opts.isReposted ? styles.ctrlIconReposted : defaultCtrlColor
(opts.isReposted
? styles.ctrlIconReposted
: defaultCtrlColor) as ViewStyle
}
strokeWidth={2.4}
size={opts.big ? 24 : 20}
@ -173,12 +178,15 @@ export function PostCtrls(opts: PostCtrlsOpts) {
<Animated.View style={anim2Style}>
{opts.isUpvoted ? (
<HeartIconSolid
style={[styles.ctrlIconUpvoted]}
style={styles.ctrlIconUpvoted as ViewStyle}
size={opts.big ? 22 : 16}
/>
) : (
<HeartIcon
style={[defaultCtrlColor, opts.big ? styles.mt1 : undefined]}
style={[
defaultCtrlColor as ViewStyle,
opts.big ? styles.mt1 : undefined,
]}
strokeWidth={3}
size={opts.big ? 20 : 16}
/>
@ -214,7 +222,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
{
color:
theme.colorScheme === 'light' ? colors.gray4 : colors.gray5,
},
} as FontAwesomeIconStyle,
]}
/>
</PostDropdownBtn>

View File

@ -6,7 +6,10 @@ import {
TouchableOpacity,
View,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {UserAvatar} from './UserAvatar'
import {Text} from './text/Text'
import {MagnifyingGlassIcon} from '../../lib/icons'
@ -92,7 +95,11 @@ export const ViewHeader = observer(function ViewHeader({
<ActivityIndicator />
) : (
<>
<FontAwesomeIcon icon="signal" style={pal.text} size={16} />
<FontAwesomeIcon
icon="signal"
style={pal.text as FontAwesomeIconStyle}
size={16}
/>
<FontAwesomeIcon
icon="x"
style={[

View File

@ -6,7 +6,10 @@ import {
View,
ViewStyle,
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {Text} from '../text/Text'
import {useTheme} from '../../../lib/ThemeContext'
import {usePalette} from '../../../lib/hooks/usePalette'
@ -28,7 +31,11 @@ export function ErrorMessage({
<View testID="errorMessageView" style={[styles.outer, pal.view, style]}>
<View
style={[styles.errorIcon, {backgroundColor: theme.palette.error.icon}]}>
<FontAwesomeIcon icon="exclamation" style={pal.text} size={16} />
<FontAwesomeIcon
icon="exclamation"
style={pal.text as FontAwesomeIconStyle}
size={16}
/>
</View>
<Text
type="sm"

View File

@ -1,6 +1,9 @@
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {
FontAwesomeIcon,
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import {Text} from '../text/Text'
import {colors} from '../../../lib/styles'
import {useTheme} from '../../../lib/ThemeContext'
@ -58,7 +61,11 @@ export function ErrorScreen({
testID="errorScreenTryAgainButton"
style={[styles.btn, {backgroundColor: theme.palette.error.icon}]}
onPress={onPressTryAgain}>
<FontAwesomeIcon icon="arrows-rotate" style={pal.text} size={16} />
<FontAwesomeIcon
icon="arrows-rotate"
style={pal.text as FontAwesomeIconStyle}
size={16}
/>
<Text type="button" style={[styles.btnText, pal.text]}>
Try again
</Text>

View File

@ -37,7 +37,7 @@ export function DropdownButton({
menuWidth,
children,
}: {
type: DropdownButtonType
type?: DropdownButtonType
style?: StyleProp<ViewStyle>
items: DropdownItem[]
label?: string

View File

@ -30,6 +30,7 @@ export function AutoSizedImage({
}: {
uri: string
onPress?: () => void
onLongPress?: () => void
style?: StyleProp<ImageStyle>
containerStyle?: StyleProp<ViewStyle>
}) {
@ -68,7 +69,7 @@ export function AutoSizedImage({
})
}
let calculatedStyle: StyleProp<ViewStyle> | undefined
let calculatedStyle: StyleProp<ImageStyle> | undefined
if (imgInfo && containerInfo) {
// imgInfo.height / imgInfo.width = x / containerInfo.width
// x = imgInfo.height / imgInfo.width * containerInfo.width

View File

@ -13,7 +13,7 @@ import {DELAY_PRESS_IN} from './constants'
interface Dim {
width: number
height: numberPressIn
height: number
}
export type ImageLayoutGridType = 'two' | 'three' | 'four'
@ -28,6 +28,7 @@ export function ImageLayoutGrid({
type: ImageLayoutGridType
uris: string[]
onPress?: (index: number) => void
onLongPress?: (index: number) => void
style?: StyleProp<ViewStyle>
}) {
const [containerInfo, setContainerInfo] = React.useState<Dim | undefined>()
@ -64,6 +65,7 @@ function ImageLayoutGridInner({
type: ImageLayoutGridType
uris: string[]
onPress?: (index: number) => void
onLongPress?: (index: number) => void
containerInfo: Dim
}) {
const size1 = React.useMemo<ImageStyle>(() => {
@ -91,14 +93,14 @@ function ImageLayoutGridInner({
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(0)}
onLongPress={() => onLongPress(0)}>
onLongPress={() => onLongPress?.(0)}>
<Image source={{uri: uris[0]}} style={size1} />
</TouchableOpacity>
<View style={styles.wSpace} />
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(1)}
onLongPress={() => onLongPress(1)}>
onLongPress={() => onLongPress?.(1)}>
<Image source={{uri: uris[1]}} style={size1} />
</TouchableOpacity>
</View>
@ -110,7 +112,7 @@ function ImageLayoutGridInner({
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(0)}
onLongPress={() => onLongPress(0)}>
onLongPress={() => onLongPress?.(0)}>
<Image source={{uri: uris[0]}} style={size2} />
</TouchableOpacity>
<View style={styles.wSpace} />
@ -118,14 +120,14 @@ function ImageLayoutGridInner({
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(1)}
onLongPress={() => onLongPress(1)}>
onLongPress={() => onLongPress?.(1)}>
<Image source={{uri: uris[1]}} style={size1} />
</TouchableOpacity>
<View style={styles.hSpace} />
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(2)}
onLongPress={() => onLongPress(2)}>
onLongPress={() => onLongPress?.(2)}>
<Image source={{uri: uris[2]}} style={size1} />
</TouchableOpacity>
</View>
@ -139,14 +141,14 @@ function ImageLayoutGridInner({
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(0)}
onLongPress={() => onLongPress(0)}>
onLongPress={() => onLongPress?.(0)}>
<Image source={{uri: uris[0]}} style={size1} />
</TouchableOpacity>
<View style={styles.hSpace} />
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(1)}
onLongPress={() => onLongPress(1)}>
onLongPress={() => onLongPress?.(1)}>
<Image source={{uri: uris[1]}} style={size1} />
</TouchableOpacity>
</View>
@ -155,14 +157,14 @@ function ImageLayoutGridInner({
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(2)}
onLongPress={() => onLongPress(2)}>
onLongPress={() => onLongPress?.(2)}>
<Image source={{uri: uris[2]}} style={size1} />
</TouchableOpacity>
<View style={styles.hSpace} />
<TouchableOpacity
delayPressIn={DELAY_PRESS_IN}
onPress={() => onPress?.(3)}
onLongPress={() => onLongPress(3)}>
onLongPress={() => onLongPress?.(3)}>
<Image source={{uri: uris[3]}} style={size1} />
</TouchableOpacity>
</View>

View File

@ -1,5 +1,5 @@
import React from 'react'
import {StyleProp, ViewStyle} from 'react-native'
import {StyleProp, TextStyle, ViewStyle} from 'react-native'
import Svg, {Path} from 'react-native-svg'
export function GridIcon({
@ -428,12 +428,21 @@ export function CommentBottomArrow({
size?: string | number
strokeWidth?: number
}) {
let color = 'currentColor'
if (
style &&
typeof style === 'object' &&
'color' in style &&
typeof style.color === 'string'
) {
color = style.color
}
return (
<Svg
fill="none"
viewBox="0 0 24 24"
strokeWidth={strokeWidth || 2.5}
stroke={style?.color || 'currentColor'}
stroke={color}
width={size || 24}
height={size || 24}
style={style}>

View File

@ -21,7 +21,7 @@ export type ScreenParams = {
navIdx: string
params: Record<string, any>
visible: boolean
scrollElRef?: MutableRefObject<FlatList<any> | undefined>
scrollElRef?: MutableRefObject<FlatList<any> | null>
}
export type Route = [React.FC<ScreenParams>, string, IconProp, RegExp]
export type MatchResult = {

View File

@ -5,14 +5,13 @@ import {ThemeProvider} from '../lib/ThemeContext'
import {PaletteColorName} from '../lib/ThemeContext'
import {usePalette} from '../lib/hooks/usePalette'
import {s} from '../lib/styles'
import {DEF_AVATAR} from '../lib/assets'
import {displayNotification} from '../lib/notifee'
import {Text} from '../com/util/text/Text'
import {ViewSelector} from '../com/util/ViewSelector'
import {EmptyState} from '../com/util/EmptyState'
import * as LoadingPlaceholder from '../com/util/LoadingPlaceholder'
import {Button} from '../com/util/forms/Button'
import {Button, ButtonType} from '../com/util/forms/Button'
import {DropdownButton, DropdownItem} from '../com/util/forms/DropdownButton'
import {ToggleButton} from '../com/util/forms/ToggleButton'
import {RadioGroup} from '../com/util/forms/RadioGroup'
@ -48,9 +47,9 @@ function DebugInner({
const [currentView, setCurrentView] = React.useState<number>(0)
const pal = usePalette('default')
const renderItem = (item, i) => {
const renderItem = (item: any) => {
return (
<View key={`view-${i}`}>
<View key={`view-${item.currentView}`}>
<View style={[s.pt10, s.pl10, s.pr10]}>
<ToggleButton
type="default-light"
@ -179,18 +178,10 @@ function NotifsView() {
"Hello world! This is a test of the notifications card. The text is long to see how that's handled.",
)
}
const triggerImg = () => {
displayNotification(
'Paul Frazee liked your post',
"Hello world! This is a test of the notifications card. The text is long to see how that's handled.",
DEF_AVATAR,
)
}
return (
<View style={s.p10}>
<View style={s.flexRow}>
<Button onPress={trigger} label="Trigger" />
<Button onPress={triggerImg} label="Trigger w/image" style={s.ml5} />
</View>
</View>
)
@ -484,14 +475,14 @@ const RADIO_BUTTON_ITEMS = [
]
function RadioButtonsView() {
const defaultPal = usePalette('default')
const [rgType, setRgType] = React.useState('default-light')
const [rgType, setRgType] = React.useState<ButtonType>('default-light')
return (
<View style={[defaultPal.view]}>
<RadioGroup
type={rgType}
items={RADIO_BUTTON_ITEMS}
initialSelection="default-light"
onSelect={setRgType}
onSelect={v => setRgType(v as ButtonType)}
/>
</View>
)

View File

@ -1,55 +1,57 @@
import React from 'react'
import {Pressable, View, StyleSheet} from 'react-native'
import {View} from 'react-native'
export const NavItem: React.FC<{label: string; screen: string}> = ({
label,
screen,
}) => {
const Link = <></> // TODO
return (
<View>
<Pressable
style={state => [
// @ts-ignore it does exist! (react-native-web) -prf
state.hovered && styles.navItemHovered,
]}>
<Link
style={[
styles.navItemLink,
false /* TODO route.name === screen*/ && styles.navItemLinkSelected,
]}
to={{screen, params: {}}}>
{label}
</Link>
</Pressable>
</View>
)
}
// export const NavItem: React.FC<{label: string; screen: string}> = ({
// label,
// screen,
// }) => {
// const Link = <></> // TODO
// return (
// <View>
// <Pressable
// style={state => [
// // @ts-ignore it does exist! (react-native-web) -prf
// state.hovered && styles.navItemHovered,
// ]}>
// <Link
// style={[
// styles.navItemLink,
// false /* TODO route.name === screen*/ && styles.navItemLinkSelected,
// ]}
// to={{screen, params: {}}}>
// {label}
// </Link>
// </Pressable>
// </View>
// )
// }
export const DesktopLeftColumn: React.FC = () => {
return (
<View style={styles.container}>
<NavItem screen="Home" label="Home" />
<NavItem screen="Search" label="Search" />
<NavItem screen="Notifications" label="Notifications" />
</View>
)
// TODO
return <View />
// return (
// <View style={styles.container}>
// <NavItem screen="Home" label="Home" />
// <NavItem screen="Search" label="Search" />
// <NavItem screen="Notifications" label="Notifications" />
// </View>
// )
}
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 'calc(50vw - 500px)',
width: '200px',
height: '100%',
},
navItemHovered: {
backgroundColor: 'gray',
},
navItemLink: {
padding: '1rem',
},
navItemLinkSelected: {
color: 'blue',
},
})
// const styles = StyleSheet.create({
// container: {
// position: 'absolute',
// left: 'calc(50vw - 500px)',
// width: '200px',
// height: '100%',
// },
// navItemHovered: {
// backgroundColor: 'gray',
// },
// navItemLink: {
// padding: '1rem',
// },
// navItemLinkSelected: {
// color: 'blue',
// },
// })

View File

@ -36,11 +36,12 @@ export const TabsSelector = observer(
undefined,
)
const closeInterp = useAnimatedValue(0)
const tabsContainerRef = useRef<View>(null)
const tabsRef = useRef<ScrollView>(null)
const tabRefs = useMemo(
() =>
Array.from({length: store.nav.tabs.length}).map(() =>
createRef<Animated.View>(),
createRef<View>(),
),
[store.nav.tabs.length],
)
@ -90,9 +91,9 @@ export const TabsSelector = observer(
const onLayout = () => {
// focus the current tab
const targetTab = tabRefs[store.nav.tabIndex]
if (tabsRef.current && targetTab.current) {
if (tabsContainerRef.current && tabsRef.current && targetTab.current) {
targetTab.current.measureLayout?.(
tabsRef.current,
tabsContainerRef.current,
(_left: number, top: number) => {
tabsRef.current?.scrollTo({y: top, animated: false})
},
@ -162,7 +163,9 @@ export const TabsSelector = observer(
</TouchableWithoutFeedback>
</View>
</View>
<View style={[s.p10, styles.section, styles.sectionGrayBg]}>
<View
ref={tabsContainerRef}
style={[s.p10, styles.section, styles.sectionGrayBg]}>
<ScrollView ref={tabsRef} style={styles.tabs}>
{store.nav.tabs.map((tab, tabIndex) => {
const {icon} = match(tab.current.url)

View File

@ -67,29 +67,36 @@ const Btn = ({
onLongPress?: (event: GestureResponderEvent) => void
}) => {
const pal = usePalette('default')
let size = 24
let addedStyles
let IconEl
let iconEl
if (icon === 'menu') {
IconEl = GridIcon
iconEl = <GridIcon style={[styles.ctrlIcon, pal.text]} />
} else if (icon === 'menu-solid') {
IconEl = GridIconSolid
iconEl = <GridIconSolid style={[styles.ctrlIcon, pal.text]} />
} else if (icon === 'home') {
IconEl = HomeIcon
size = 27
iconEl = <HomeIcon size={27} style={[styles.ctrlIcon, pal.text]} />
} else if (icon === 'home-solid') {
IconEl = HomeIconSolid
size = 27
iconEl = <HomeIconSolid size={27} style={[styles.ctrlIcon, pal.text]} />
} else if (icon === 'bell') {
IconEl = BellIcon
size = 27
addedStyles = {position: 'relative', top: -1} as ViewStyle
const addedStyles = {position: 'relative', top: -1} as ViewStyle
iconEl = (
<BellIcon size={27} style={[styles.ctrlIcon, pal.text, addedStyles]} />
)
} else if (icon === 'bell-solid') {
IconEl = BellIconSolid
size = 27
addedStyles = {position: 'relative', top: -1} as ViewStyle
const addedStyles = {position: 'relative', top: -1} as ViewStyle
iconEl = (
<BellIconSolid
size={27}
style={[styles.ctrlIcon, pal.text, addedStyles]}
/>
)
} else {
IconEl = FontAwesomeIcon
iconEl = (
<FontAwesomeIcon
size={24}
icon={icon}
style={[styles.ctrlIcon, pal.text]}
/>
)
}
return (
@ -108,11 +115,7 @@ const Btn = ({
<Text style={styles.tabCountLabel}>{tabCount}</Text>
</View>
) : undefined}
<IconEl
size={size}
style={[styles.ctrlIcon, pal.text, addedStyles]}
icon={icon}
/>
{iconEl}
</TouchableOpacity>
)
}
@ -122,7 +125,7 @@ export const MobileShell: React.FC = observer(() => {
const pal = usePalette('default')
const store = useStores()
const [isTabsSelectorActive, setTabsSelectorActive] = useState(false)
const scrollElRef = useRef<FlatList | undefined>()
const scrollElRef = useRef<FlatList>(null)
const winDim = useWindowDimensions()
const [menuSwipingDirection, setMenuSwipingDirection] = useState(0)
const swipeGestureInterp = useAnimatedValue(0)
@ -292,9 +295,11 @@ export const MobileShell: React.FC = observer(() => {
)
shouldRenderMenu = true
}
const menuSwipeTransform = {
const menuSwipeTransform = menuTranslateX
? {
transform: [{translateX: menuTranslateX}],
}
: undefined
const swipeOpacity = {
opacity: swipeGestureInterp.interpolate({
inputRange: [-1, 0, 1],
@ -417,10 +422,7 @@ export const MobileShell: React.FC = observer(() => {
) : undefined}
{shouldRenderMenu && (
<Animated.View style={[styles.menuDrawer, menuSwipeTransform]}>
<Menu
visible={isMenuActive}
onClose={() => store.shell.setMainMenuOpen(false)}
/>
<Menu onClose={() => store.shell.setMainMenuOpen(false)} />
</Animated.View>
)}
</HorzSwipe>