diff --git a/src/App.native.tsx b/src/App.native.tsx
index a532a08d..fa523cd8 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -5,6 +5,7 @@ import {RootSiblingParent} from 'react-native-root-siblings'
import {GestureHandlerRootView} from 'react-native-gesture-handler'
import SplashScreen from 'react-native-splash-screen'
import {SafeAreaProvider} from 'react-native-safe-area-context'
+import {ThemeProvider} from './view/lib/ThemeContext'
import * as view from './view/index'
import {RootStoreModel, setupState, RootStoreProvider} from './state'
import {MobileShell} from './view/shell/mobile'
@@ -40,9 +41,11 @@ function App() {
-
-
-
+
+
+
+
+
diff --git a/src/lib/functions.ts b/src/lib/functions.ts
new file mode 100644
index 00000000..d6fbf5b9
--- /dev/null
+++ b/src/lib/functions.ts
@@ -0,0 +1,6 @@
+export function choose>(
+ value: keyof T,
+ choices: T,
+): U {
+ return choices[value]
+}
diff --git a/src/view/com/composer/Autocomplete.tsx b/src/view/com/composer/Autocomplete.tsx
index 1637108f..b151e0d9 100644
--- a/src/view/com/composer/Autocomplete.tsx
+++ b/src/view/com/composer/Autocomplete.tsx
@@ -5,8 +5,8 @@ import {
StyleSheet,
useWindowDimensions,
} from 'react-native'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
-import {Text} from '../util/Text'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
+import {Text} from '../util/text/Text'
import {colors} from '../../lib/styles'
interface AutocompleteItem {
diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx
index baa93110..fe310f19 100644
--- a/src/view/com/composer/ComposePost.tsx
+++ b/src/view/com/composer/ComposePost.tsx
@@ -15,7 +15,7 @@ import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view'
import {Autocomplete} from './Autocomplete'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import * as Toast from '../util/Toast'
// @ts-ignore no type definition -prf
import ProgressCircle from 'react-native-progress/Circle'
diff --git a/src/view/com/composer/Prompt.tsx b/src/view/com/composer/Prompt.tsx
index 2b1559df..ec63d950 100644
--- a/src/view/com/composer/Prompt.tsx
+++ b/src/view/com/composer/Prompt.tsx
@@ -3,7 +3,7 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {colors} from '../../lib/styles'
import {useStores} from '../../../state'
import {UserAvatar} from '../util/UserAvatar'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
export function ComposePrompt({
noAvi = false,
diff --git a/src/view/com/discover/SuggestedFollows.tsx b/src/view/com/discover/SuggestedFollows.tsx
index b78bae88..77bd94d5 100644
--- a/src/view/com/discover/SuggestedFollows.tsx
+++ b/src/view/com/discover/SuggestedFollows.tsx
@@ -10,9 +10,9 @@ import LinearGradient from 'react-native-linear-gradient'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {observer} from 'mobx-react-lite'
import _omit from 'lodash.omit'
-import {ErrorScreen} from '../util/ErrorScreen'
+import {ErrorScreen} from '../util/error/ErrorScreen'
import {Link} from '../util/Link'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import {UserAvatar} from '../util/UserAvatar'
import * as Toast from '../util/Toast'
import {useStores} from '../../../state'
diff --git a/src/view/com/lightbox/Lightbox.tsx b/src/view/com/lightbox/Lightbox.tsx
index 36c51764..849354ae 100644
--- a/src/view/com/lightbox/Lightbox.tsx
+++ b/src/view/com/lightbox/Lightbox.tsx
@@ -10,7 +10,7 @@ import {observer} from 'mobx-react-lite'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {SwipeAndZoom, Dir} from '../util/gestures/SwipeAndZoom'
import {useStores} from '../../../state'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
import * as models from '../../../state/models/shell-ui'
diff --git a/src/view/com/login/CreateAccount.tsx b/src/view/com/login/CreateAccount.tsx
index f97eb7a0..689a4f38 100644
--- a/src/view/com/login/CreateAccount.tsx
+++ b/src/view/com/login/CreateAccount.tsx
@@ -15,7 +15,7 @@ import * as EmailValidator from 'email-validator'
import {Logo} from './Logo'
import {Picker} from '../util/Picker'
import {TextLink} from '../util/Link'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import {s, colors} from '../../lib/styles'
import {
makeValidHandle,
diff --git a/src/view/com/login/Signin.tsx b/src/view/com/login/Signin.tsx
index 45728d3b..f76507d7 100644
--- a/src/view/com/login/Signin.tsx
+++ b/src/view/com/login/Signin.tsx
@@ -12,7 +12,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import * as EmailValidator from 'email-validator'
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
import {Logo} from './Logo'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import {s, colors} from '../../lib/styles'
import {createFullHandle, toNiceDomain} from '../../../lib/strings'
import {useStores, RootStoreModel, DEFAULT_SERVICE} from '../../../state'
diff --git a/src/view/com/modals/Confirm.tsx b/src/view/com/modals/Confirm.tsx
index a18043f1..7545e36a 100644
--- a/src/view/com/modals/Confirm.tsx
+++ b/src/view/com/modals/Confirm.tsx
@@ -6,10 +6,10 @@ import {
View,
} from 'react-native'
import LinearGradient from 'react-native-linear-gradient'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
import {s, colors, gradients} from '../../lib/styles'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {ErrorMessage} from '../util/error/ErrorMessage'
export const snapPoints = ['50%']
diff --git a/src/view/com/modals/CreateScene.tsx b/src/view/com/modals/CreateScene.tsx
index 0d47aa4b..60c24054 100644
--- a/src/view/com/modals/CreateScene.tsx
+++ b/src/view/com/modals/CreateScene.tsx
@@ -9,8 +9,8 @@ import {
import LinearGradient from 'react-native-linear-gradient'
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
import {AppBskyActorCreateScene} from '@atproto/api'
-import {ErrorMessage} from '../util/ErrorMessage'
-import {Text} from '../util/Text'
+import {ErrorMessage} from '../util/error/ErrorMessage'
+import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
import {s, colors, gradients} from '../../lib/styles'
import {
diff --git a/src/view/com/modals/EditProfile.tsx b/src/view/com/modals/EditProfile.tsx
index bd97ced5..8a3f016a 100644
--- a/src/view/com/modals/EditProfile.tsx
+++ b/src/view/com/modals/EditProfile.tsx
@@ -9,8 +9,8 @@ import {
import LinearGradient from 'react-native-linear-gradient'
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
import {Image as PickedImage} from 'react-native-image-crop-picker'
-import {Text} from '../util/Text'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {Text} from '../util/text/Text'
+import {ErrorMessage} from '../util/error/ErrorMessage'
import {useStores} from '../../../state'
import {ProfileViewModel} from '../../../state/models/profile-view'
import {s, colors, gradients} from '../../lib/styles'
diff --git a/src/view/com/modals/InviteToScene.tsx b/src/view/com/modals/InviteToScene.tsx
index 28380b6a..a7344017 100644
--- a/src/view/com/modals/InviteToScene.tsx
+++ b/src/view/com/modals/InviteToScene.tsx
@@ -19,8 +19,8 @@ import {
import _omit from 'lodash.omit'
import {AtUri} from '../../../third-party/uri'
import {ProfileCard} from '../profile/ProfileCard'
-import {ErrorMessage} from '../util/ErrorMessage'
-import {Text} from '../util/Text'
+import {ErrorMessage} from '../util/error/ErrorMessage'
+import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
import * as apilib from '../../../state/lib/api'
import {ProfileViewModel} from '../../../state/models/profile-view'
diff --git a/src/view/com/modals/ReportAccount.tsx b/src/view/com/modals/ReportAccount.tsx
index 582e2423..bf4d5f5a 100644
--- a/src/view/com/modals/ReportAccount.tsx
+++ b/src/view/com/modals/ReportAccount.tsx
@@ -9,8 +9,8 @@ import LinearGradient from 'react-native-linear-gradient'
import {useStores} from '../../../state'
import {s, colors, gradients} from '../../lib/styles'
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
-import {Text} from '../util/Text'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {Text} from '../util/text/Text'
+import {ErrorMessage} from '../util/error/ErrorMessage'
const ITEMS: RadioGroupItem[] = [
{key: 'spam', label: 'Spam or excessive repeat posts'},
diff --git a/src/view/com/modals/ReportPost.tsx b/src/view/com/modals/ReportPost.tsx
index 6f134032..d4684069 100644
--- a/src/view/com/modals/ReportPost.tsx
+++ b/src/view/com/modals/ReportPost.tsx
@@ -9,8 +9,8 @@ import LinearGradient from 'react-native-linear-gradient'
import {useStores} from '../../../state'
import {s, colors, gradients} from '../../lib/styles'
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
-import {Text} from '../util/Text'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {Text} from '../util/text/Text'
+import {ErrorMessage} from '../util/error/ErrorMessage'
const ITEMS: RadioGroupItem[] = [
{key: 'spam', label: 'Spam or excessive repeat posts'},
diff --git a/src/view/com/modals/ServerInput.tsx b/src/view/com/modals/ServerInput.tsx
index 0d1e0e91..884fb91e 100644
--- a/src/view/com/modals/ServerInput.tsx
+++ b/src/view/com/modals/ServerInput.tsx
@@ -2,7 +2,7 @@ import React, {useState} from 'react'
import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
-import {Text} from '../util/Text'
+import {Text} from '../util/text/Text'
import {useStores} from '../../../state'
import {s, colors} from '../../lib/styles'
import {
diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx
index c986bca5..91a01db4 100644
--- a/src/view/com/notifications/Feed.tsx
+++ b/src/view/com/notifications/Feed.tsx
@@ -4,9 +4,9 @@ import {View, FlatList} from 'react-native'
import {NotificationsViewModel} from '../../../state/models/notifications-view'
import {FeedItem} from './FeedItem'
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {ErrorMessage} from '../util/error/ErrorMessage'
import {EmptyState} from '../util/EmptyState'
-import {OnScrollCb} from '../../lib/useOnMainScroll'
+import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
@@ -54,7 +54,6 @@ export const Feed = observer(function Feed({
{view.isLoading && !data && }
{view.hasError && (
}
{feed.hasError && (
)}
{dropdownItems?.length ? (
-
-
+
) : undefined}
diff --git a/src/view/com/profile/ProfileMembers.tsx b/src/view/com/profile/ProfileMembers.tsx
index 251ece41..0e34865b 100644
--- a/src/view/com/profile/ProfileMembers.tsx
+++ b/src/view/com/profile/ProfileMembers.tsx
@@ -3,7 +3,7 @@ import {observer} from 'mobx-react-lite'
import {ActivityIndicator, FlatList, View} from 'react-native'
import {MembersViewModel, MemberItem} from '../../../state/models/members-view'
import {ProfileCard} from './ProfileCard'
-import {ErrorMessage} from '../util/ErrorMessage'
+import {ErrorMessage} from '../util/error/ErrorMessage'
import {useStores} from '../../../state'
export const ProfileMembers = observer(function ProfileMembers({
@@ -49,7 +49,6 @@ export const ProfileMembers = observer(function ProfileMembers({
return (
}) {
+ const pal = usePalette('default')
return (
{icon === 'user-group' ? (
) : (
-
+
)}
- {message}
+
+ {message}
+
)
}
@@ -40,12 +47,9 @@ const styles = StyleSheet.create({
icon: {
marginLeft: 'auto',
marginRight: 'auto',
- color: colors.gray3,
},
text: {
textAlign: 'center',
- color: colors.gray5,
paddingTop: 16,
- fontSize: 16,
},
})
diff --git a/src/view/com/util/ErrorMessage.tsx b/src/view/com/util/ErrorMessage.tsx
deleted file mode 100644
index b87b77ba..00000000
--- a/src/view/com/util/ErrorMessage.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import React from 'react'
-import {
- StyleSheet,
- TouchableOpacity,
- StyleProp,
- View,
- ViewStyle,
-} from 'react-native'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import LinearGradient from 'react-native-linear-gradient'
-import {Text} from './Text'
-import {colors, gradients} from '../../lib/styles'
-
-export function ErrorMessage({
- message,
- numberOfLines,
- dark,
- style,
- onPressTryAgain,
-}: {
- message: string
- numberOfLines?: number
- dark?: boolean
- style?: StyleProp
- onPressTryAgain?: () => void
-}) {
- const inner = (
- <>
-
-
-
-
- {message}
-
- {onPressTryAgain && (
-
-
-
- )}
- >
- )
- if (dark) {
- return (
-
- {inner}
-
- )
- }
- return {inner}
-}
-
-const styles = StyleSheet.create({
- outer: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: colors.red1,
- borderWidth: 1,
- borderColor: colors.red3,
- borderRadius: 6,
- paddingVertical: 8,
- paddingHorizontal: 8,
- },
- errorIcon: {
- backgroundColor: colors.red4,
- borderRadius: 12,
- width: 24,
- height: 24,
- alignItems: 'center',
- justifyContent: 'center',
- marginRight: 8,
- },
- darkErrorIcon: {
- backgroundColor: colors.white,
- },
- message: {
- flex: 1,
- color: colors.red4,
- paddingRight: 10,
- },
- darkMessage: {
- color: colors.white,
- fontWeight: '600',
- },
- btn: {
- paddingHorizontal: 4,
- paddingVertical: 4,
- },
-})
diff --git a/src/view/com/util/FloatingActionButton.tsx b/src/view/com/util/FloatingActionButton.tsx
deleted file mode 100644
index 21c4fba6..00000000
--- a/src/view/com/util/FloatingActionButton.tsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react'
-import {
- GestureResponderEvent,
- StyleSheet,
- TouchableWithoutFeedback,
- View,
-} from 'react-native'
-import LinearGradient from 'react-native-linear-gradient'
-import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {IconProp} from '@fortawesome/fontawesome-svg-core'
-import {colors, gradients} from '../../lib/styles'
-import * as zIndex from '../../lib/z-index'
-
-type OnPress = ((event: GestureResponderEvent) => void) | undefined
-export function FAB({icon, onPress}: {icon: IconProp; onPress: OnPress}) {
- return (
-
-
-
-
-
-
-
- )
-}
-
-const styles = StyleSheet.create({
- outer: {
- position: 'absolute',
- zIndex: zIndex.FAB,
- right: 22,
- bottom: 14,
- width: 60,
- height: 60,
- borderRadius: 30,
- shadowColor: '#000',
- shadowOpacity: 0.3,
- shadowOffset: {width: 0, height: 1},
- },
- inner: {
- width: 60,
- height: 60,
- borderRadius: 30,
- justifyContent: 'center',
- alignItems: 'center',
- },
- icon: {},
-})
diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx
index 2bb55357..05573d99 100644
--- a/src/view/com/util/Link.tsx
+++ b/src/view/com/util/Link.tsx
@@ -9,7 +9,8 @@ import {
View,
ViewStyle,
} from 'react-native'
-import {Text} from './Text'
+import {Text} from './text/Text'
+import {TypographyVariant} from '../../lib/ThemeContext'
import {useStores, RootStoreModel} from '../../../state'
import {convertBskyAppUrlIfNeeded} from '../../../lib/strings'
@@ -57,14 +58,14 @@ export const Link = observer(function Link({
})
export const TextLink = observer(function Link({
+ type = 'body1',
style,
href,
- title,
text,
}: {
+ type: TypographyVariant
style?: StyleProp
href: string
- title?: string
text: string
}) {
const store = useStores()
@@ -75,7 +76,7 @@ export const TextLink = observer(function Link({
handleLink(store, href, true)
}
return (
-
+
{text}
)
diff --git a/src/view/com/util/LoadingPlaceholder.tsx b/src/view/com/util/LoadingPlaceholder.tsx
index 9c2d0398..15488167 100644
--- a/src/view/com/util/LoadingPlaceholder.tsx
+++ b/src/view/com/util/LoadingPlaceholder.tsx
@@ -3,6 +3,7 @@ import {StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {UpIcon} from '../../lib/icons'
import {s, colors} from '../../lib/styles'
+import {useTheme} from '../../lib/ThemeContext'
export function LoadingPlaceholder({
width,
@@ -13,13 +14,14 @@ export function LoadingPlaceholder({
height: string | number
style?: StyleProp
}) {
+ const theme = useTheme()
return (
@@ -41,6 +43,7 @@ export function PostLoadingPlaceholder({
}: {
style?: StyleProp
}) {
+ const theme = useTheme()
return (
@@ -52,16 +55,24 @@ export function PostLoadingPlaceholder({
-
+
-
+
@@ -125,8 +136,6 @@ export function NotificationFeedLoadingPlaceholder() {
const styles = StyleSheet.create({
post: {
flexDirection: 'row',
- backgroundColor: colors.white,
- borderRadius: 6,
padding: 10,
margin: 1,
},
@@ -135,8 +144,6 @@ const styles = StyleSheet.create({
marginRight: 10,
},
notification: {
- backgroundColor: colors.white,
- borderRadius: 6,
padding: 10,
paddingLeft: 46,
margin: 1,
diff --git a/src/view/com/util/Picker.tsx b/src/view/com/util/Picker.tsx
index 84a627b6..208ec049 100644
--- a/src/view/com/util/Picker.tsx
+++ b/src/view/com/util/Picker.tsx
@@ -1,3 +1,5 @@
+// TODO: replaceme with something in the design system
+
import React, {useRef} from 'react'
import {
StyleProp,
@@ -13,7 +15,7 @@ import {
FontAwesomeIconStyle,
} from '@fortawesome/react-native-fontawesome'
import RootSiblings from 'react-native-root-siblings'
-import {Text} from './Text'
+import {Text} from './text/Text'
import {colors} from '../../lib/styles'
interface PickerItem {
diff --git a/src/view/com/util/PostCtrls.tsx b/src/view/com/util/PostCtrls.tsx
index 26421076..ac10e92f 100644
--- a/src/view/com/util/PostCtrls.tsx
+++ b/src/view/com/util/PostCtrls.tsx
@@ -2,10 +2,10 @@ import React from 'react'
import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'
-import {Text} from './Text'
+import {Text} from './text/Text'
import {UpIcon, UpIconSolid} from '../../lib/icons'
import {s, colors} from '../../lib/styles'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
interface PostCtrlsOpts {
big?: boolean
diff --git a/src/view/com/util/PostEmbeds.tsx b/src/view/com/util/PostEmbeds.tsx
index 1c980465..839110a2 100644
--- a/src/view/com/util/PostEmbeds.tsx
+++ b/src/view/com/util/PostEmbeds.tsx
@@ -2,7 +2,7 @@ import React from 'react'
import {ImageStyle, StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api'
import {Link} from '../util/Link'
-import {Text} from '../util/Text'
+import {Text} from './text/Text'
import {colors} from '../../lib/styles'
import {AutoSizedImage} from './images/AutoSizedImage'
import {ImagesLightbox} from '../../../state/models/shell-ui'
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index 77dfbb48..fae3a4c8 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -2,8 +2,8 @@ import React from 'react'
import {StyleSheet, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {Link} from '../util/Link'
-import {Text} from '../util/Text'
-import {PostDropdownBtn} from '../util/DropdownBtn'
+import {Text} from './text/Text'
+import {PostDropdownBtn} from './forms/DropdownButton'
import {s} from '../../lib/styles'
import {ago} from '../../../lib/strings'
diff --git a/src/view/com/util/Selector.tsx b/src/view/com/util/Selector.tsx
index 954360b3..211c5b90 100644
--- a/src/view/com/util/Selector.tsx
+++ b/src/view/com/util/Selector.tsx
@@ -5,7 +5,7 @@ import {
TouchableWithoutFeedback,
View,
} from 'react-native'
-import {Text} from './Text'
+import {Text} from './text/Text'
import {colors} from '../../lib/styles'
interface Layout {
diff --git a/src/view/com/util/Text.tsx b/src/view/com/util/Text.tsx
deleted file mode 100644
index acf7589e..00000000
--- a/src/view/com/util/Text.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react'
-import {Text as RNText, TextProps} from 'react-native'
-import {s} from '../../lib/styles'
-
-export function Text({
- children,
- style,
- ...props
-}: React.PropsWithChildren) {
- return (
-
- {children}
-
- )
-}
diff --git a/src/view/com/util/UserInfoText.tsx b/src/view/com/util/UserInfoText.tsx
index cdd1f4d9..f5ed07d6 100644
--- a/src/view/com/util/UserInfoText.tsx
+++ b/src/view/com/util/UserInfoText.tsx
@@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react'
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
import {StyleProp, TextStyle} from 'react-native'
import {Link} from './Link'
-import {Text} from './Text'
+import {Text} from './text/Text'
import {LoadingPlaceholder} from './LoadingPlaceholder'
import {useStores} from '../../../state'
diff --git a/src/view/com/util/ViewHeader.tsx b/src/view/com/util/ViewHeader.tsx
index e14c2412..c6eaba5d 100644
--- a/src/view/com/util/ViewHeader.tsx
+++ b/src/view/com/util/ViewHeader.tsx
@@ -8,7 +8,7 @@ import {
} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {UserAvatar} from './UserAvatar'
-import {Text} from './Text'
+import {Text} from './text/Text'
import {s, colors} from '../../lib/styles'
import {MagnifyingGlassIcon} from '../../lib/icons'
import {useStores} from '../../../state'
diff --git a/src/view/com/util/ViewSelector.tsx b/src/view/com/util/ViewSelector.tsx
index e436e41b..a29ee9d2 100644
--- a/src/view/com/util/ViewSelector.tsx
+++ b/src/view/com/util/ViewSelector.tsx
@@ -7,8 +7,8 @@ import {
} from 'react-native'
import {Selector} from './Selector'
import {HorzSwipe} from './gestures/HorzSwipe'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
-import {OnScrollCb} from '../../lib/useOnMainScroll'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
+import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
const HEADER_ITEM = {_reactKey: '__header__'}
const SELECTOR_ITEM = {_reactKey: '__selector__'}
diff --git a/src/view/com/util/error/ErrorMessage.tsx b/src/view/com/util/error/ErrorMessage.tsx
new file mode 100644
index 00000000..905268d3
--- /dev/null
+++ b/src/view/com/util/error/ErrorMessage.tsx
@@ -0,0 +1,76 @@
+import React from 'react'
+import {
+ StyleSheet,
+ TouchableOpacity,
+ StyleProp,
+ View,
+ ViewStyle,
+} from 'react-native'
+import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
+import {Text} from '../text/Text'
+import {colors} from '../../../lib/styles'
+import {useTheme} from '../../../lib/ThemeContext'
+import {usePalette} from '../../../lib/hooks/usePalette'
+
+export function ErrorMessage({
+ message,
+ numberOfLines,
+ style,
+ onPressTryAgain,
+}: {
+ message: string
+ numberOfLines?: number
+ style?: StyleProp
+ onPressTryAgain?: () => void
+}) {
+ const theme = useTheme()
+ const pal = usePalette('error')
+ return (
+
+
+
+
+
+ {message}
+
+ {onPressTryAgain && (
+
+
+
+ )}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ outer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ paddingVertical: 8,
+ paddingHorizontal: 8,
+ },
+ errorIcon: {
+ borderRadius: 12,
+ width: 24,
+ height: 24,
+ alignItems: 'center',
+ justifyContent: 'center',
+ marginRight: 8,
+ },
+ message: {
+ flex: 1,
+ paddingRight: 10,
+ },
+ btn: {
+ paddingHorizontal: 4,
+ paddingVertical: 4,
+ },
+})
diff --git a/src/view/com/util/ErrorScreen.tsx b/src/view/com/util/error/ErrorScreen.tsx
similarity index 56%
rename from src/view/com/util/ErrorScreen.tsx
rename to src/view/com/util/error/ErrorScreen.tsx
index d0e1e275..6db54a9f 100644
--- a/src/view/com/util/ErrorScreen.tsx
+++ b/src/view/com/util/error/ErrorScreen.tsx
@@ -1,8 +1,10 @@
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Text} from './Text'
-import {colors} from '../../lib/styles'
+import {Text} from '../text/Text'
+import {colors} from '../../../lib/styles'
+import {useTheme} from '../../../lib/ThemeContext'
+import {usePalette} from '../../../lib/hooks/usePalette'
export function ErrorScreen({
title,
@@ -15,10 +17,16 @@ export function ErrorScreen({
details?: string
onPressTryAgain?: () => void
}) {
+ const theme = useTheme()
+ const pal = usePalette('error')
return (
-
+
-
+
- {title}
- {message}
- {details && {details}}
+
+ {title}
+
+ {message}
+ {details && (
+
+ {details}
+
+ )}
{onPressTryAgain && (
-
-
- Try again
+
+
+
+ Try again
+
)}
@@ -48,32 +68,19 @@ export function ErrorScreen({
const styles = StyleSheet.create({
outer: {
flex: 1,
- backgroundColor: colors.red1,
- borderWidth: 1,
- borderColor: colors.red3,
- borderRadius: 6,
paddingVertical: 30,
paddingHorizontal: 14,
- margin: 10,
},
title: {
textAlign: 'center',
- color: colors.red4,
- fontSize: 24,
marginBottom: 10,
},
message: {
textAlign: 'center',
- color: colors.red4,
marginBottom: 20,
},
details: {
textAlign: 'center',
- color: colors.black,
- backgroundColor: colors.white,
- borderWidth: 1,
- borderColor: colors.gray5,
- borderRadius: 6,
paddingVertical: 10,
paddingHorizontal: 14,
overflow: 'hidden',
@@ -85,23 +92,17 @@ const styles = StyleSheet.create({
btn: {
flexDirection: 'row',
alignItems: 'center',
- backgroundColor: colors.red4,
- borderRadius: 6,
paddingHorizontal: 16,
paddingVertical: 10,
},
btnText: {
marginLeft: 5,
- color: colors.white,
- fontSize: 16,
- fontWeight: 'bold',
},
errorIconContainer: {
alignItems: 'center',
marginBottom: 10,
},
errorIcon: {
- backgroundColor: colors.red4,
borderRadius: 30,
width: 50,
height: 50,
diff --git a/src/view/com/util/forms/Button.tsx b/src/view/com/util/forms/Button.tsx
new file mode 100644
index 00000000..b5c4da19
--- /dev/null
+++ b/src/view/com/util/forms/Button.tsx
@@ -0,0 +1,120 @@
+import React from 'react'
+import {
+ StyleProp,
+ StyleSheet,
+ TextStyle,
+ TouchableOpacity,
+ ViewStyle,
+} from 'react-native'
+import {Text} from '../text/Text'
+import {useTheme} from '../../../lib/ThemeContext'
+import {choose} from '../../../../lib/functions'
+
+export type ButtonType =
+ | 'primary'
+ | 'secondary'
+ | 'inverted'
+ | 'primary-outline'
+ | 'secondary-outline'
+ | 'primary-light'
+ | 'secondary-light'
+ | 'default-light'
+
+export function Button({
+ type = 'primary',
+ label,
+ style,
+ onPress,
+ children,
+}: React.PropsWithChildren<{
+ type?: ButtonType
+ label?: string
+ style?: StyleProp
+ onPress?: () => void
+}>) {
+ const theme = useTheme()
+ const outerStyle = choose>(type, {
+ primary: {
+ backgroundColor: theme.palette.primary.background,
+ },
+ secondary: {
+ backgroundColor: theme.palette.secondary.background,
+ },
+ inverted: {
+ backgroundColor: theme.palette.inverted.background,
+ },
+ 'primary-outline': {
+ backgroundColor: theme.palette.default.background,
+ borderWidth: 1,
+ borderColor: theme.palette.primary.border,
+ },
+ 'secondary-outline': {
+ backgroundColor: theme.palette.default.background,
+ borderWidth: 1,
+ borderColor: theme.palette.secondary.border,
+ },
+ 'primary-light': {
+ backgroundColor: theme.palette.default.background,
+ },
+ 'secondary-light': {
+ backgroundColor: theme.palette.default.background,
+ },
+ 'default-light': {
+ backgroundColor: theme.palette.default.background,
+ },
+ })
+ const labelStyle = choose>(type, {
+ primary: {
+ color: theme.palette.primary.text,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ secondary: {
+ color: theme.palette.secondary.text,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ inverted: {
+ color: theme.palette.inverted.text,
+ fontWeight: theme.palette.inverted.isLowContrast ? '500' : undefined,
+ },
+ 'primary-outline': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-outline': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'primary-light': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-light': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'default-light': {
+ color: theme.palette.default.text,
+ fontWeight: theme.palette.default.isLowContrast ? '500' : undefined,
+ },
+ })
+ return (
+
+ {label ? (
+
+ {label}
+
+ ) : (
+ children
+ )}
+
+ )
+}
+
+const styles = StyleSheet.create({
+ outer: {
+ paddingHorizontal: 10,
+ paddingVertical: 8,
+ },
+})
diff --git a/src/view/com/util/DropdownBtn.tsx b/src/view/com/util/forms/DropdownButton.tsx
similarity index 81%
rename from src/view/com/util/DropdownBtn.tsx
rename to src/view/com/util/forms/DropdownButton.tsx
index 3c642193..c81ccf6c 100644
--- a/src/view/com/util/DropdownBtn.tsx
+++ b/src/view/com/util/forms/DropdownButton.tsx
@@ -11,12 +11,13 @@ import {
import {IconProp} from '@fortawesome/fontawesome-svg-core'
import RootSiblings from 'react-native-root-siblings'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Text} from './Text'
-import {colors} from '../../lib/styles'
-import {toShareUrl} from '../../../lib/strings'
-import {useStores} from '../../../state'
-import {ReportPostModal, ConfirmModal} from '../../../state/models/shell-ui'
-import {TABS_ENABLED} from '../../../build-flags'
+import {Text} from '../text/Text'
+import {Button, ButtonType} from './Button'
+import {colors} from '../../../lib/styles'
+import {toShareUrl} from '../../../../lib/strings'
+import {useStores} from '../../../../state'
+import {ReportPostModal, ConfirmModal} from '../../../../state/models/shell-ui'
+import {TABS_ENABLED} from '../../../../build-flags'
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
@@ -26,14 +27,20 @@ export interface DropdownItem {
onPress: () => void
}
-export function DropdownBtn({
+export type DropdownButtonType = ButtonType | 'bare'
+
+export function DropdownButton({
+ type = 'bare',
style,
items,
+ label,
menuWidth,
children,
}: {
+ type: DropdownButtonType
style?: StyleProp
items: DropdownItem[]
+ label?: string
menuWidth?: number
children?: React.ReactNode
}) {
@@ -62,14 +69,23 @@ export function DropdownBtn({
)
}
+ if (type === 'bare') {
+ return (
+
+ {children}
+
+ )
+ }
return (
-
- {children}
-
+
+
+
)
}
@@ -77,7 +93,6 @@ export function PostDropdownBtn({
style,
children,
itemHref,
- itemTitle,
isAuthor,
onCopyPostText,
onDeletePost,
@@ -141,9 +156,9 @@ export function PostDropdownBtn({
].filter(Boolean) as DropdownItem[]
return (
-
+
{children}
-
+
)
}
diff --git a/src/view/com/util/forms/RadioButton.tsx b/src/view/com/util/forms/RadioButton.tsx
index 9da404be..81489c44 100644
--- a/src/view/com/util/forms/RadioButton.tsx
+++ b/src/view/com/util/forms/RadioButton.tsx
@@ -1,24 +1,126 @@
import React from 'react'
-import {StyleSheet, TouchableOpacity, View} from 'react-native'
-import {Text} from '../Text'
-import {colors} from '../../../lib/styles'
+import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
+import {Text} from '../text/Text'
+import {Button, ButtonType} from './Button'
+import {useTheme} from '../../../lib/ThemeContext'
+import {choose} from '../../../../lib/functions'
export function RadioButton({
+ type = 'default-light',
label,
isSelected,
+ style,
onPress,
}: {
+ type?: ButtonType
label: string
isSelected: boolean
+ style?: StyleProp
onPress: () => void
}) {
+ const theme = useTheme()
+ const circleStyle = choose>(type, {
+ primary: {
+ borderColor: theme.palette.primary.text,
+ },
+ secondary: {
+ borderColor: theme.palette.secondary.text,
+ },
+ inverted: {
+ borderColor: theme.palette.inverted.text,
+ },
+ 'primary-outline': {
+ borderColor: theme.palette.primary.border,
+ },
+ 'secondary-outline': {
+ borderColor: theme.palette.secondary.border,
+ },
+ 'primary-light': {
+ borderColor: theme.palette.primary.border,
+ },
+ 'secondary-light': {
+ borderColor: theme.palette.secondary.border,
+ },
+ 'default-light': {
+ borderColor: theme.palette.default.border,
+ },
+ })
+ const circleFillStyle = choose>(
+ type,
+ {
+ primary: {
+ backgroundColor: theme.palette.primary.text,
+ },
+ secondary: {
+ backgroundColor: theme.palette.secondary.text,
+ },
+ inverted: {
+ backgroundColor: theme.palette.inverted.text,
+ },
+ 'primary-outline': {
+ backgroundColor: theme.palette.primary.background,
+ },
+ 'secondary-outline': {
+ backgroundColor: theme.palette.secondary.background,
+ },
+ 'primary-light': {
+ backgroundColor: theme.palette.primary.background,
+ },
+ 'secondary-light': {
+ backgroundColor: theme.palette.secondary.background,
+ },
+ 'default-light': {
+ backgroundColor: theme.palette.primary.background,
+ },
+ },
+ )
+ const labelStyle = choose>(type, {
+ primary: {
+ color: theme.palette.primary.text,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ secondary: {
+ color: theme.palette.secondary.text,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ inverted: {
+ color: theme.palette.inverted.text,
+ fontWeight: theme.palette.inverted.isLowContrast ? '500' : undefined,
+ },
+ 'primary-outline': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-outline': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'primary-light': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-light': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'default-light': {
+ color: theme.palette.default.text,
+ fontWeight: theme.palette.default.isLowContrast ? '500' : undefined,
+ },
+ })
return (
-
-
- {isSelected ? : undefined}
+
+
)
}
@@ -26,30 +128,21 @@ const styles = StyleSheet.create({
outer: {
flexDirection: 'row',
alignItems: 'center',
- marginBottom: 5,
- borderRadius: 8,
- borderWidth: 1,
- borderColor: colors.gray2,
- paddingHorizontal: 10,
- paddingVertical: 8,
},
circle: {
- width: 30,
- height: 30,
+ width: 26,
+ height: 26,
borderRadius: 15,
padding: 4,
borderWidth: 1,
- borderColor: colors.gray3,
marginRight: 10,
},
circleFill: {
- width: 20,
- height: 20,
+ width: 16,
+ height: 16,
borderRadius: 10,
- backgroundColor: colors.blue3,
},
label: {
flex: 1,
- fontSize: 17,
},
})
diff --git a/src/view/com/util/forms/RadioGroup.tsx b/src/view/com/util/forms/RadioGroup.tsx
index 6684cde5..9abc2345 100644
--- a/src/view/com/util/forms/RadioGroup.tsx
+++ b/src/view/com/util/forms/RadioGroup.tsx
@@ -1,6 +1,7 @@
import React, {useState} from 'react'
import {View} from 'react-native'
import {RadioButton} from './RadioButton'
+import {ButtonType} from './Button'
export interface RadioGroupItem {
label: string
@@ -8,22 +9,28 @@ export interface RadioGroupItem {
}
export function RadioGroup({
+ type,
items,
+ initialSelection = '',
onSelect,
}: {
+ type?: ButtonType
items: RadioGroupItem[]
+ initialSelection?: string
onSelect: (key: string) => void
}) {
- const [selection, setSelection] = useState('')
+ const [selection, setSelection] = useState(initialSelection)
const onSelectInner = (key: string) => {
setSelection(key)
onSelect(key)
}
return (
- {items.map(item => (
+ {items.map((item, i) => (
onSelectInner(item.key)}
diff --git a/src/view/com/util/forms/ToggleButton.tsx b/src/view/com/util/forms/ToggleButton.tsx
new file mode 100644
index 00000000..77e8fa20
--- /dev/null
+++ b/src/view/com/util/forms/ToggleButton.tsx
@@ -0,0 +1,165 @@
+import React from 'react'
+import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
+import {Text} from '../text/Text'
+import {Button, ButtonType} from './Button'
+import {useTheme} from '../../../lib/ThemeContext'
+import {choose} from '../../../../lib/functions'
+import {colors} from '../../../lib/styles'
+
+export function ToggleButton({
+ type = 'default-light',
+ label,
+ isSelected,
+ style,
+ onPress,
+}: {
+ type?: ButtonType
+ label: string
+ isSelected: boolean
+ style?: StyleProp
+ onPress: () => void
+}) {
+ const theme = useTheme()
+ const circleStyle = choose>(type, {
+ primary: {
+ borderColor: theme.palette.primary.text,
+ },
+ secondary: {
+ borderColor: theme.palette.secondary.text,
+ },
+ inverted: {
+ borderColor: theme.palette.inverted.text,
+ },
+ 'primary-outline': {
+ borderColor: theme.palette.primary.border,
+ },
+ 'secondary-outline': {
+ borderColor: theme.palette.secondary.border,
+ },
+ 'primary-light': {
+ borderColor: theme.palette.primary.border,
+ },
+ 'secondary-light': {
+ borderColor: theme.palette.secondary.border,
+ },
+ 'default-light': {
+ borderColor: theme.palette.default.border,
+ },
+ })
+ const circleFillStyle = choose>(
+ type,
+ {
+ primary: {
+ backgroundColor: theme.palette.primary.text,
+ opacity: isSelected ? 1 : 0.33,
+ },
+ secondary: {
+ backgroundColor: theme.palette.secondary.text,
+ opacity: isSelected ? 1 : 0.33,
+ },
+ inverted: {
+ backgroundColor: theme.palette.inverted.text,
+ opacity: isSelected ? 1 : 0.33,
+ },
+ 'primary-outline': {
+ backgroundColor: theme.palette.primary.background,
+ opacity: isSelected ? 1 : 0.5,
+ },
+ 'secondary-outline': {
+ backgroundColor: theme.palette.secondary.background,
+ opacity: isSelected ? 1 : 0.5,
+ },
+ 'primary-light': {
+ backgroundColor: theme.palette.primary.background,
+ opacity: isSelected ? 1 : 0.5,
+ },
+ 'secondary-light': {
+ backgroundColor: theme.palette.secondary.background,
+ opacity: isSelected ? 1 : 0.5,
+ },
+ 'default-light': {
+ backgroundColor: isSelected
+ ? theme.palette.primary.background
+ : colors.gray3,
+ },
+ },
+ )
+ const labelStyle = choose>(type, {
+ primary: {
+ color: theme.palette.primary.text,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ secondary: {
+ color: theme.palette.secondary.text,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ inverted: {
+ color: theme.palette.inverted.text,
+ fontWeight: theme.palette.inverted.isLowContrast ? '500' : undefined,
+ },
+ 'primary-outline': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-outline': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'primary-light': {
+ color: theme.palette.primary.textInverted,
+ fontWeight: theme.palette.primary.isLowContrast ? '500' : undefined,
+ },
+ 'secondary-light': {
+ color: theme.palette.secondary.textInverted,
+ fontWeight: theme.palette.secondary.isLowContrast ? '500' : undefined,
+ },
+ 'default-light': {
+ color: theme.palette.default.text,
+ fontWeight: theme.palette.default.isLowContrast ? '500' : undefined,
+ },
+ })
+ return (
+
+ )
+}
+
+const styles = StyleSheet.create({
+ outer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ circle: {
+ width: 42,
+ height: 26,
+ borderRadius: 15,
+ padding: 4,
+ borderWidth: 1,
+ marginRight: 10,
+ },
+ circleFill: {
+ width: 16,
+ height: 16,
+ borderRadius: 10,
+ },
+ circleFillSelected: {
+ marginLeft: 16,
+ },
+ label: {
+ flex: 1,
+ },
+})
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx
index 05425eb3..9de443b7 100644
--- a/src/view/com/util/images/AutoSizedImage.tsx
+++ b/src/view/com/util/images/AutoSizedImage.tsx
@@ -9,7 +9,7 @@ import {
View,
ViewStyle,
} from 'react-native'
-import {Text} from '../Text'
+import {Text} from '../text/Text'
import {colors} from '../../../lib/styles'
const MAX_HEIGHT = 300
diff --git a/src/view/com/util/RichText.tsx b/src/view/com/util/text/RichText.tsx
similarity index 81%
rename from src/view/com/util/RichText.tsx
rename to src/view/com/util/text/RichText.tsx
index d6f193f9..c9ed4b58 100644
--- a/src/view/com/util/RichText.tsx
+++ b/src/view/com/util/text/RichText.tsx
@@ -1,9 +1,11 @@
import React from 'react'
import {TextStyle, StyleProp} from 'react-native'
-import {TextLink} from './Link'
+import {TextLink} from '../Link'
import {Text} from './Text'
-import {s} from '../../lib/styles'
-import {toShortUrl} from '../../../lib/strings'
+import {s} from '../../../lib/styles'
+import {toShortUrl} from '../../../../lib/strings'
+import {TypographyVariant} from '../../../lib/ThemeContext'
+import {usePalette} from '../../../lib/hooks/usePalette'
type TextSlice = {start: number; end: number}
type Entity = {
@@ -13,16 +15,19 @@ type Entity = {
}
export function RichText({
+ type = 'body1',
text,
entities,
style,
numberOfLines,
}: {
+ type: TypographyVariant
text: string
entities?: Entity[]
style?: StyleProp
numberOfLines?: number
}) {
+ const pal = usePalette('default')
if (!entities?.length) {
if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) {
style = {
@@ -47,18 +52,20 @@ export function RichText({
els.push(
,
)
} else if (segment.entity.type === 'link') {
els.push(
,
)
}
@@ -66,7 +73,7 @@ export function RichText({
key++
}
return (
-
+
{els}
)
diff --git a/src/view/com/util/text/Text.tsx b/src/view/com/util/text/Text.tsx
new file mode 100644
index 00000000..549eb290
--- /dev/null
+++ b/src/view/com/util/text/Text.tsx
@@ -0,0 +1,23 @@
+import React from 'react'
+import {Text as RNText, TextProps} from 'react-native'
+import {s} from '../../../lib/styles'
+import {useTheme, TypographyVariant} from '../../../lib/ThemeContext'
+
+export type CustomTextProps = TextProps & {
+ type?: TypographyVariant
+}
+
+export function Text({
+ type = 'body1',
+ children,
+ style,
+ ...props
+}: React.PropsWithChildren) {
+ const theme = useTheme()
+ const typography = theme.typography[type]
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/view/lib/ThemeContext.tsx b/src/view/lib/ThemeContext.tsx
new file mode 100644
index 00000000..57f758c5
--- /dev/null
+++ b/src/view/lib/ThemeContext.tsx
@@ -0,0 +1,70 @@
+import React, {createContext, useContext, useMemo} from 'react'
+import {TextStyle, useColorScheme, ViewStyle} from 'react-native'
+import {darkTheme, defaultTheme} from './themes'
+
+export type ColorScheme = 'light' | 'dark'
+
+export type PaletteColorName =
+ | 'default'
+ | 'primary'
+ | 'secondary'
+ | 'inverted'
+ | 'error'
+export type PaletteColor = {
+ isLowContrast: boolean
+ background: string
+ backgroundLight: string
+ text: string
+ textLight: string
+ textInverted: string
+ link: string
+ border: string
+ icon: string
+}
+export type Palette = Record
+
+export type ShapeName = 'button' | 'bigButton' | 'smallButton'
+export type Shapes = Record
+
+export type TypographyVariant =
+ | 'h1'
+ | 'h2'
+ | 'h3'
+ | 'h4'
+ | 'subtitle1'
+ | 'subtitle2'
+ | 'body1'
+ | 'body2'
+ | 'button'
+ | 'caption'
+ | 'overline'
+export type Typography = Record
+
+export interface Theme {
+ colorScheme: ColorScheme
+ palette: Palette
+ shapes: Shapes
+ typography: Typography
+}
+
+export interface ThemeProviderProps {
+ theme?: ColorScheme
+}
+
+export const ThemeContext = createContext(defaultTheme)
+
+export const useTheme = () => useContext(ThemeContext)
+
+export const ThemeProvider: React.FC = ({
+ theme,
+ children,
+}) => {
+ const colorScheme = useColorScheme()
+
+ const value = useMemo(
+ () => ((theme || colorScheme) === 'dark' ? darkTheme : defaultTheme),
+ [colorScheme, theme],
+ )
+
+ return {children}
+}
diff --git a/src/view/lib/useAnimatedValue.ts b/src/view/lib/hooks/useAnimatedValue.ts
similarity index 100%
rename from src/view/lib/useAnimatedValue.ts
rename to src/view/lib/hooks/useAnimatedValue.ts
diff --git a/src/view/lib/useOnMainScroll.ts b/src/view/lib/hooks/useOnMainScroll.ts
similarity index 94%
rename from src/view/lib/useOnMainScroll.ts
rename to src/view/lib/hooks/useOnMainScroll.ts
index ee008122..c3c16ff8 100644
--- a/src/view/lib/useOnMainScroll.ts
+++ b/src/view/lib/hooks/useOnMainScroll.ts
@@ -1,6 +1,6 @@
import {useState} from 'react'
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
-import {RootStoreModel} from '../../state'
+import {RootStoreModel} from '../../../state'
export type OnScrollCb = (
event: NativeSyntheticEvent,
diff --git a/src/view/lib/hooks/usePalette.ts b/src/view/lib/hooks/usePalette.ts
new file mode 100644
index 00000000..e9af4ae1
--- /dev/null
+++ b/src/view/lib/hooks/usePalette.ts
@@ -0,0 +1,41 @@
+import {TextStyle, ViewStyle} from 'react-native'
+import {useTheme, PaletteColorName, PaletteColor} from '../ThemeContext'
+
+export interface UsePaletteValue {
+ colors: PaletteColor
+ view: ViewStyle
+ border: ViewStyle
+ text: TextStyle
+ textLight: TextStyle
+ textInverted: TextStyle
+ link: TextStyle
+}
+export function usePalette(color: PaletteColorName): UsePaletteValue {
+ const palette = useTheme().palette[color]
+ return {
+ colors: palette,
+ view: {
+ backgroundColor: palette.background,
+ },
+ border: {
+ borderWidth: 1,
+ borderColor: palette.border,
+ },
+ text: {
+ color: palette.text,
+ fontWeight: palette.isLowContrast ? '500' : undefined,
+ },
+ textLight: {
+ color: palette.textLight,
+ fontWeight: palette.isLowContrast ? '500' : undefined,
+ },
+ textInverted: {
+ color: palette.textInverted,
+ fontWeight: palette.isLowContrast ? '500' : undefined,
+ },
+ link: {
+ color: palette.link,
+ fontWeight: palette.isLowContrast ? '500' : undefined,
+ },
+ }
+}
diff --git a/src/view/lib/themes.ts b/src/view/lib/themes.ts
new file mode 100644
index 00000000..3851ee9d
--- /dev/null
+++ b/src/view/lib/themes.ts
@@ -0,0 +1,163 @@
+import type {Theme} from './ThemeContext'
+import {colors} from './styles'
+
+export const defaultTheme: Theme = {
+ colorScheme: 'light',
+ palette: {
+ default: {
+ isLowContrast: false,
+ background: colors.white,
+ backgroundLight: colors.gray2,
+ text: colors.black,
+ textLight: colors.gray5,
+ textInverted: colors.white,
+ link: colors.blue3,
+ border: colors.gray3,
+ icon: colors.gray2,
+ },
+ primary: {
+ isLowContrast: true,
+ background: colors.blue3,
+ backgroundLight: colors.blue2,
+ text: colors.white,
+ textLight: colors.blue0,
+ textInverted: colors.blue3,
+ link: colors.blue0,
+ border: colors.blue4,
+ icon: colors.blue4,
+ },
+ secondary: {
+ isLowContrast: true,
+ background: colors.green3,
+ backgroundLight: colors.green2,
+ text: colors.white,
+ textLight: colors.green1,
+ textInverted: colors.green4,
+ link: colors.green1,
+ border: colors.green4,
+ icon: colors.green4,
+ },
+ inverted: {
+ isLowContrast: true,
+ background: colors.black,
+ backgroundLight: colors.gray6,
+ text: colors.white,
+ textLight: colors.gray3,
+ textInverted: colors.black,
+ link: colors.blue2,
+ border: colors.gray3,
+ icon: colors.gray5,
+ },
+ error: {
+ isLowContrast: true,
+ background: colors.red3,
+ backgroundLight: colors.red2,
+ text: colors.white,
+ textLight: colors.red1,
+ textInverted: colors.red3,
+ link: colors.red1,
+ border: colors.red4,
+ icon: colors.red4,
+ },
+ },
+ shapes: {
+ button: {
+ // TODO
+ },
+ bigButton: {
+ // TODO
+ },
+ smallButton: {
+ // TODO
+ },
+ },
+ typography: {
+ h1: {
+ fontSize: 48,
+ fontWeight: '500',
+ },
+ h2: {
+ fontSize: 34,
+ letterSpacing: 0.25,
+ fontWeight: '500',
+ },
+ h3: {
+ fontSize: 24,
+ fontWeight: '500',
+ },
+ h4: {
+ fontWeight: '500',
+ fontSize: 20,
+ letterSpacing: 0.15,
+ },
+ subtitle1: {
+ fontSize: 16,
+ letterSpacing: 0.15,
+ },
+ subtitle2: {
+ fontWeight: '500',
+ fontSize: 14,
+ letterSpacing: 0.1,
+ },
+ body1: {
+ fontSize: 16,
+ letterSpacing: 0.5,
+ },
+ body2: {
+ fontSize: 14,
+ letterSpacing: 0.25,
+ },
+ button: {
+ fontWeight: '500',
+ fontSize: 14,
+ letterSpacing: 0.5,
+ },
+ caption: {
+ fontSize: 12,
+ letterSpacing: 0.4,
+ },
+ overline: {
+ fontSize: 10,
+ letterSpacing: 1.5,
+ textTransform: 'uppercase',
+ },
+ },
+}
+
+export const darkTheme: Theme = {
+ ...defaultTheme,
+ colorScheme: 'dark',
+ palette: {
+ ...defaultTheme.palette,
+ default: {
+ isLowContrast: true,
+ background: colors.black,
+ backgroundLight: colors.gray6,
+ text: colors.white,
+ textLight: colors.gray3,
+ textInverted: colors.black,
+ link: colors.blue2,
+ border: colors.gray3,
+ icon: colors.gray5,
+ },
+ primary: {
+ ...defaultTheme.palette.primary,
+ textInverted: colors.blue2,
+ },
+ secondary: {
+ ...defaultTheme.palette.secondary,
+ textInverted: colors.green2,
+ },
+ inverted: {
+ isLowContrast: false,
+ background: colors.white,
+ backgroundLight: colors.gray2,
+ text: colors.black,
+ textLight: colors.gray5,
+ textInverted: colors.white,
+ link: colors.blue3,
+ border: colors.gray3,
+ icon: colors.gray1,
+ },
+ },
+}
diff --git a/src/view/lib/z-index.ts b/src/view/lib/z-index.ts
deleted file mode 100644
index 872027d3..00000000
--- a/src/view/lib/z-index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export const FAB = 1
-export const BASE = 0
diff --git a/src/view/routes.ts b/src/view/routes.ts
index 272a1b09..3717e0f0 100644
--- a/src/view/routes.ts
+++ b/src/view/routes.ts
@@ -15,6 +15,7 @@ import {ProfileFollowers} from './screens/ProfileFollowers'
import {ProfileFollows} from './screens/ProfileFollows'
import {ProfileMembers} from './screens/ProfileMembers'
import {Settings} from './screens/Settings'
+import {Debug} from './screens/Debug'
export type ScreenParams = {
navIdx: [number, number]
@@ -71,6 +72,7 @@ export const routes: Route[] = [
'retweet',
r('/profile/(?[^/]+)/post/(?[^/]+)/reposted-by'),
],
+ [Debug, 'Debug', 'house', r('/debug')],
]
export function match(url: string): MatchResult {
diff --git a/src/view/screens/Contacts.tsx b/src/view/screens/Contacts.tsx
index bcfb4778..8de56d79 100644
--- a/src/view/screens/Contacts.tsx
+++ b/src/view/screens/Contacts.tsx
@@ -3,11 +3,11 @@ import {StyleSheet, TextInput, View} from 'react-native'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
import {Selector} from '../com/util/Selector'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {colors} from '../lib/styles'
import {ScreenParams} from '../routes'
import {useStores} from '../../state'
-import {useAnimatedValue} from '../lib/useAnimatedValue'
+import {useAnimatedValue} from '../lib/hooks/useAnimatedValue'
export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
const store = useStores()
diff --git a/src/view/screens/Debug.tsx b/src/view/screens/Debug.tsx
new file mode 100644
index 00000000..f34bcc17
--- /dev/null
+++ b/src/view/screens/Debug.tsx
@@ -0,0 +1,432 @@
+import React from 'react'
+import {ScrollView, View} from 'react-native'
+import {ViewHeader} from '../com/util/ViewHeader'
+import {ThemeProvider} from '../lib/ThemeContext'
+import {PaletteColorName} from '../lib/ThemeContext'
+import {usePalette} from '../lib/hooks/usePalette'
+
+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 {DropdownButton, DropdownItem} from '../com/util/forms/DropdownButton'
+import {ToggleButton} from '../com/util/forms/ToggleButton'
+import {RadioGroup} from '../com/util/forms/RadioGroup'
+import {ErrorScreen} from '../com/util/error/ErrorScreen'
+import {ErrorMessage} from '../com/util/error/ErrorMessage'
+
+const MAIN_VIEWS = ['Base', 'Controls', 'Error']
+
+export const Debug = () => {
+ const [colorScheme, setColorScheme] = React.useState<'light' | 'dark'>(
+ 'light',
+ )
+ const onToggleColorScheme = () => {
+ setColorScheme(colorScheme === 'light' ? 'dark' : 'light')
+ }
+ return (
+
+
+
+ )
+}
+
+function DebugInner({
+ colorScheme,
+ onToggleColorScheme,
+}: {
+ colorScheme: 'light' | 'dark'
+ onToggleColorScheme: () => void
+}) {
+ const [currentView, setCurrentView] = React.useState(0)
+ const pal = usePalette('default')
+
+ const renderItem = item => {
+ return (
+
+
+
+
+ {item.currentView === 2 ? (
+
+ ) : item.currentView === 1 ? (
+
+ ) : (
+
+ )}
+
+ )
+ }
+
+ const items = [{currentView}]
+
+ return (
+
+
+
+
+ )
+}
+
+function Heading({label}: {label: string}) {
+ const pal = usePalette('default')
+ return (
+
+
+ {label}
+
+
+ )
+}
+
+function BaseView() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ControlsView() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+function ErrorView() {
+ return (
+
+
+ {}}
+ />
+
+
+
+
+
+
+
+
+ {}}
+ />
+
+
+ {}}
+ numberOfLines={1}
+ />
+
+
+ )
+}
+
+function PaletteView({palette}: {palette: PaletteColorName}) {
+ const defaultPal = usePalette('default')
+ const pal = usePalette(palette)
+ return (
+
+ {palette} colors
+ Light text
+ Link text
+ {palette !== 'default' && (
+
+ Inverted text
+
+ )}
+
+ )
+}
+
+function TypographyView() {
+ const pal = usePalette('default')
+ return (
+
+
+ Heading 1
+
+
+ Heading 2
+
+
+ Heading 3
+
+
+ Heading 4
+
+
+ Subtitle 1
+
+
+ Subtitle 2
+
+
+ Body 1
+
+
+ Body 2
+
+
+ Button
+
+
+ Caption
+
+
+ Overline
+
+
+ )
+}
+
+function EmptyStateView() {
+ return
+}
+
+function LoadingPlaceholderView() {
+ return (
+ <>
+
+
+ >
+ )
+}
+
+function ButtonsView() {
+ const defaultPal = usePalette('default')
+ const buttonStyles = {marginRight: 5}
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const DROPDOWN_ITEMS: DropdownItem[] = [
+ {
+ icon: ['far', 'paste'],
+ label: 'Copy post text',
+ onPress() {},
+ },
+ {
+ icon: 'share',
+ label: 'Share...',
+ onPress() {},
+ },
+ {
+ icon: 'circle-exclamation',
+ label: 'Report post',
+ onPress() {},
+ },
+]
+function DropdownButtonsView() {
+ const defaultPal = usePalette('default')
+ return (
+
+
+
+
+
+
+ Bare
+
+
+
+ )
+}
+
+function ToggleButtonsView() {
+ const defaultPal = usePalette('default')
+ const buttonStyles = {marginBottom: 5}
+ const [isSelected, setIsSelected] = React.useState(false)
+ const onToggle = () => setIsSelected(!isSelected)
+ return (
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const RADIO_BUTTON_ITEMS = [
+ {key: 'default-light', label: 'Default Light'},
+ {key: 'primary', label: 'Primary'},
+ {key: 'secondary', label: 'Secondary'},
+ {key: 'inverted', label: 'Inverted'},
+ {key: 'primary-outline', label: 'Primary Outline'},
+ {key: 'secondary-outline', label: 'Secondary Outline'},
+ {key: 'primary-light', label: 'Primary Light'},
+ {key: 'secondary-light', label: 'Secondary Light'},
+]
+function RadioButtonsView() {
+ const defaultPal = usePalette('default')
+ const [rgType, setRgType] = React.useState('default-light')
+ return (
+
+
+
+ )
+}
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 834010b0..118ba9ed 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -6,11 +6,11 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/posts/Feed'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {useStores} from '../../state'
import {ScreenParams} from '../routes'
import {s, colors} from '../lib/styles'
-import {useOnMainScroll} from '../lib/useOnMainScroll'
+import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
import {clamp} from 'lodash'
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
diff --git a/src/view/screens/Login.tsx b/src/view/screens/Login.tsx
index 734903d7..0315e287 100644
--- a/src/view/screens/Login.tsx
+++ b/src/view/screens/Login.tsx
@@ -10,7 +10,7 @@ import {observer} from 'mobx-react-lite'
import {Signin} from '../com/login/Signin'
import {Logo} from '../com/login/Logo'
import {CreateAccount} from '../com/login/CreateAccount'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {s, colors} from '../lib/styles'
enum ScreenState {
diff --git a/src/view/screens/NotFound.tsx b/src/view/screens/NotFound.tsx
index 16d75c38..3591b696 100644
--- a/src/view/screens/NotFound.tsx
+++ b/src/view/screens/NotFound.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import {Button, View} from 'react-native'
import {ViewHeader} from '../com/util/ViewHeader'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {useStores} from '../../state'
export const NotFound = () => {
diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx
index fe4a7872..2257dd22 100644
--- a/src/view/screens/Notifications.tsx
+++ b/src/view/screens/Notifications.tsx
@@ -4,7 +4,7 @@ import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/notifications/Feed'
import {useStores} from '../../state'
import {ScreenParams} from '../routes'
-import {useOnMainScroll} from '../lib/useOnMainScroll'
+import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
export const Notifications = ({navIdx, visible}: ScreenParams) => {
const store = useStores()
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index 8d41d9ad..437f5f4a 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -12,14 +12,14 @@ import {ProfileHeader} from '../com/profile/ProfileHeader'
import {FeedItem} from '../com/posts/FeedItem'
import {ProfileCard} from '../com/profile/ProfileCard'
import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder'
-import {ErrorScreen} from '../com/util/ErrorScreen'
-import {ErrorMessage} from '../com/util/ErrorMessage'
+import {ErrorScreen} from '../com/util/error/ErrorScreen'
+import {ErrorMessage} from '../com/util/error/ErrorMessage'
import {EmptyState} from '../com/util/EmptyState'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {ViewHeader} from '../com/util/ViewHeader'
import * as Toast from '../com/util/Toast'
import {s, colors} from '../lib/styles'
-import {useOnMainScroll} from '../lib/useOnMainScroll'
+import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
const LOADING_ITEM = {_reactKey: '__loading__'}
const END_ITEM = {_reactKey: '__end__'}
@@ -116,7 +116,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
renderItem = (item: any) => (
diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx
index c909f50e..4ab1436a 100644
--- a/src/view/screens/Search.tsx
+++ b/src/view/screens/Search.tsx
@@ -10,7 +10,7 @@ import {
import {ViewHeader} from '../com/util/ViewHeader'
import {SuggestedFollows} from '../com/discover/SuggestedFollows'
import {UserAvatar} from '../com/util/UserAvatar'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {ScreenParams} from '../routes'
import {useStores} from '../../state'
import {UserAutocompleteViewModel} from '../../state/models/user-autocomplete-view'
diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx
index 1656d3b6..d3fcdfdf 100644
--- a/src/view/screens/Settings.tsx
+++ b/src/view/screens/Settings.tsx
@@ -6,7 +6,7 @@ import {ScreenParams} from '../routes'
import {s, colors} from '../lib/styles'
import {ViewHeader} from '../com/util/ViewHeader'
import {Link} from '../com/util/Link'
-import {Text} from '../com/util/Text'
+import {Text} from '../com/util/text/Text'
import {UserAvatar} from '../com/util/UserAvatar'
export const Settings = observer(function Settings({
@@ -57,6 +57,9 @@ export const Settings = observer(function Settings({
+
+ Debug tools
+
)
diff --git a/src/view/shell/mobile/Composer.tsx b/src/view/shell/mobile/Composer.tsx
index c586bc87..1a2d2d24 100644
--- a/src/view/shell/mobile/Composer.tsx
+++ b/src/view/shell/mobile/Composer.tsx
@@ -3,7 +3,7 @@ import {observer} from 'mobx-react-lite'
import {Animated, Easing, Platform, StyleSheet, View} from 'react-native'
import {ComposePost} from '../../com/composer/ComposePost'
import {ComposerOpts} from '../../../state/models/shell-ui'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
export const Composer = observer(
({
diff --git a/src/view/shell/mobile/Menu.tsx b/src/view/shell/mobile/Menu.tsx
index 8c11e3e8..a8a81b4d 100644
--- a/src/view/shell/mobile/Menu.tsx
+++ b/src/view/shell/mobile/Menu.tsx
@@ -17,7 +17,7 @@ import {
MagnifyingGlassIcon,
} from '../../lib/icons'
import {UserAvatar} from '../../com/util/UserAvatar'
-import {Text} from '../../com/util/Text'
+import {Text} from '../../com/util/text/Text'
import {CreateSceneModal} from '../../../state/models/shell-ui'
export const Menu = ({
diff --git a/src/view/shell/mobile/TabsSelector.tsx b/src/view/shell/mobile/TabsSelector.tsx
index 41b18a33..71aaa200 100644
--- a/src/view/shell/mobile/TabsSelector.tsx
+++ b/src/view/shell/mobile/TabsSelector.tsx
@@ -10,13 +10,13 @@ import {
} from 'react-native'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
-import {Text} from '../../com/util/Text'
+import {Text} from '../../com/util/text/Text'
import Swipeable from 'react-native-gesture-handler/Swipeable'
import {useStores} from '../../../state'
import {s, colors} from '../../lib/styles'
import {toShareUrl} from '../../../lib/strings'
import {match} from '../../routes'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
const TAB_HEIGHT = 42
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index 6437d696..673c0fbe 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -29,7 +29,7 @@ import {Onboard} from '../../screens/Onboard'
import {HorzSwipe} from '../../com/util/gestures/HorzSwipe'
import {Modal} from '../../com/modals/Modal'
import {Lightbox} from '../../com/lightbox/Lightbox'
-import {Text} from '../../com/util/Text'
+import {Text} from '../../com/util/text/Text'
import {TabsSelector} from './TabsSelector'
import {Composer} from './Composer'
import {s, colors} from '../../lib/styles'
@@ -42,7 +42,7 @@ import {
BellIcon,
BellIconSolid,
} from '../../lib/icons'
-import {useAnimatedValue} from '../../lib/useAnimatedValue'
+import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
const Btn = ({
icon,