Add a design system (#34)
* Add theming system * Add standard Button control and update RadioButtons * Unify radiobutton with design system * Update debug screen to have multiple views * Add ToggleButton * Update error controls to use design system * Add typography to <Text> element * Move DropdownButton into the design system * Clean out old code * Move Text into design system * Add 'inverted' color palette * Move LoadingPlaceholder into the design systemzio/stable
parent
cc63660982
commit
7e31645e9a
|
@ -5,6 +5,7 @@ import {RootSiblingParent} from 'react-native-root-siblings'
|
||||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||||
import SplashScreen from 'react-native-splash-screen'
|
import SplashScreen from 'react-native-splash-screen'
|
||||||
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
import {SafeAreaProvider} from 'react-native-safe-area-context'
|
||||||
|
import {ThemeProvider} from './view/lib/ThemeContext'
|
||||||
import * as view from './view/index'
|
import * as view from './view/index'
|
||||||
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
import {RootStoreModel, setupState, RootStoreProvider} from './state'
|
||||||
import {MobileShell} from './view/shell/mobile'
|
import {MobileShell} from './view/shell/mobile'
|
||||||
|
@ -40,9 +41,11 @@ function App() {
|
||||||
<GestureHandlerRootView style={{flex: 1}}>
|
<GestureHandlerRootView style={{flex: 1}}>
|
||||||
<RootSiblingParent>
|
<RootSiblingParent>
|
||||||
<RootStoreProvider value={rootStore}>
|
<RootStoreProvider value={rootStore}>
|
||||||
<SafeAreaProvider>
|
<ThemeProvider>
|
||||||
<MobileShell />
|
<SafeAreaProvider>
|
||||||
</SafeAreaProvider>
|
<MobileShell />
|
||||||
|
</SafeAreaProvider>
|
||||||
|
</ThemeProvider>
|
||||||
</RootStoreProvider>
|
</RootStoreProvider>
|
||||||
</RootSiblingParent>
|
</RootSiblingParent>
|
||||||
</GestureHandlerRootView>
|
</GestureHandlerRootView>
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
export function choose<U, T extends Record<string, U>>(
|
||||||
|
value: keyof T,
|
||||||
|
choices: T,
|
||||||
|
): U {
|
||||||
|
return choices[value]
|
||||||
|
}
|
|
@ -5,8 +5,8 @@ import {
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
useWindowDimensions,
|
useWindowDimensions,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../lib/styles'
|
||||||
|
|
||||||
interface AutocompleteItem {
|
interface AutocompleteItem {
|
||||||
|
|
|
@ -15,7 +15,7 @@ import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view'
|
import {UserAutocompleteViewModel} from '../../../state/models/user-autocomplete-view'
|
||||||
import {Autocomplete} from './Autocomplete'
|
import {Autocomplete} from './Autocomplete'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
// @ts-ignore no type definition -prf
|
// @ts-ignore no type definition -prf
|
||||||
import ProgressCircle from 'react-native-progress/Circle'
|
import ProgressCircle from 'react-native-progress/Circle'
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../lib/styles'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
|
|
||||||
export function ComposePrompt({
|
export function ComposePrompt({
|
||||||
noAvi = false,
|
noAvi = false,
|
||||||
|
|
|
@ -10,9 +10,9 @@ import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import _omit from 'lodash.omit'
|
import _omit from 'lodash.omit'
|
||||||
import {ErrorScreen} from '../util/ErrorScreen'
|
import {ErrorScreen} from '../util/error/ErrorScreen'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {observer} from 'mobx-react-lite'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {SwipeAndZoom, Dir} from '../util/gestures/SwipeAndZoom'
|
import {SwipeAndZoom, Dir} from '../util/gestures/SwipeAndZoom'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
import * as models from '../../../state/models/shell-ui'
|
import * as models from '../../../state/models/shell-ui'
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import * as EmailValidator from 'email-validator'
|
||||||
import {Logo} from './Logo'
|
import {Logo} from './Logo'
|
||||||
import {Picker} from '../util/Picker'
|
import {Picker} from '../util/Picker'
|
||||||
import {TextLink} from '../util/Link'
|
import {TextLink} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {
|
import {
|
||||||
makeValidHandle,
|
makeValidHandle,
|
||||||
|
|
|
@ -12,7 +12,7 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import * as EmailValidator from 'email-validator'
|
import * as EmailValidator from 'email-validator'
|
||||||
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
|
import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api'
|
||||||
import {Logo} from './Logo'
|
import {Logo} from './Logo'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {createFullHandle, toNiceDomain} from '../../../lib/strings'
|
import {createFullHandle, toNiceDomain} from '../../../lib/strings'
|
||||||
import {useStores, RootStoreModel, DEFAULT_SERVICE} from '../../../state'
|
import {useStores, RootStoreModel, DEFAULT_SERVICE} from '../../../state'
|
||||||
|
|
|
@ -6,10 +6,10 @@ import {
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
|
|
||||||
export const snapPoints = ['50%']
|
export const snapPoints = ['50%']
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ import {
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
||||||
import {AppBskyActorCreateScene} from '@atproto/api'
|
import {AppBskyActorCreateScene} from '@atproto/api'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import {
|
||||||
import LinearGradient from 'react-native-linear-gradient'
|
import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
||||||
import {Image as PickedImage} from 'react-native-image-crop-picker'
|
import {Image as PickedImage} from 'react-native-image-crop-picker'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
|
|
|
@ -19,8 +19,8 @@ import {
|
||||||
import _omit from 'lodash.omit'
|
import _omit from 'lodash.omit'
|
||||||
import {AtUri} from '../../../third-party/uri'
|
import {AtUri} from '../../../third-party/uri'
|
||||||
import {ProfileCard} from '../profile/ProfileCard'
|
import {ProfileCard} from '../profile/ProfileCard'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import * as apilib from '../../../state/lib/api'
|
import * as apilib from '../../../state/lib/api'
|
||||||
import {ProfileViewModel} from '../../../state/models/profile-view'
|
import {ProfileViewModel} from '../../../state/models/profile-view'
|
||||||
|
|
|
@ -9,8 +9,8 @@ import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
|
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
|
|
||||||
const ITEMS: RadioGroupItem[] = [
|
const ITEMS: RadioGroupItem[] = [
|
||||||
{key: 'spam', label: 'Spam or excessive repeat posts'},
|
{key: 'spam', label: 'Spam or excessive repeat posts'},
|
||||||
|
|
|
@ -9,8 +9,8 @@ import LinearGradient from 'react-native-linear-gradient'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
|
import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
|
|
||||||
const ITEMS: RadioGroupItem[] = [
|
const ITEMS: RadioGroupItem[] = [
|
||||||
{key: 'spam', label: 'Spam or excessive repeat posts'},
|
{key: 'spam', label: 'Spam or excessive repeat posts'},
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, {useState} from 'react'
|
||||||
import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {Platform, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
import {BottomSheetScrollView, BottomSheetTextInput} from '@gorhom/bottom-sheet'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {
|
import {
|
||||||
|
|
|
@ -4,9 +4,9 @@ import {View, FlatList} from 'react-native'
|
||||||
import {NotificationsViewModel} from '../../../state/models/notifications-view'
|
import {NotificationsViewModel} from '../../../state/models/notifications-view'
|
||||||
import {FeedItem} from './FeedItem'
|
import {FeedItem} from './FeedItem'
|
||||||
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {EmptyState} from '../util/EmptyState'
|
import {EmptyState} from '../util/EmptyState'
|
||||||
import {OnScrollCb} from '../../lib/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
|
|
||||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@ export const Feed = observer(function Feed({
|
||||||
{view.isLoading && !data && <NotificationFeedLoadingPlaceholder />}
|
{view.isLoading && !data && <NotificationFeedLoadingPlaceholder />}
|
||||||
{view.hasError && (
|
{view.hasError && (
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onPressTryAgain}
|
onPressTryAgain={onPressTryAgain}
|
||||||
|
|
|
@ -8,9 +8,9 @@ import {PostThreadViewModel} from '../../../state/models/post-thread-view'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {ago, pluralize} from '../../../lib/strings'
|
import {ago, pluralize} from '../../../lib/strings'
|
||||||
import {UpIconSolid} from '../../lib/icons'
|
import {UpIconSolid} from '../../lib/icons'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Post} from '../post/Post'
|
import {Post} from '../post/Post'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {InviteAccepter} from './InviteAccepter'
|
import {InviteAccepter} from './InviteAccepter'
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {ConfirmModal} from '../../../state/models/shell-ui'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {ProfileCard} from '../profile/ProfileCard'
|
import {ProfileCard} from '../profile/ProfileCard'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {s, colors, gradients} from '../../lib/styles'
|
import {s, colors, gradients} from '../../lib/styles'
|
||||||
|
|
||||||
export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
|
export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view'
|
import {TabView, SceneMap, Route, TabBarProps} from 'react-native-tab-view'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserGroupIcon} from '../../lib/icons'
|
import {UserGroupIcon} from '../../lib/icons'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s} from '../../lib/styles'
|
import {s} from '../../lib/styles'
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||||
import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {SafeAreaView, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {SuggestedFollows} from '../discover/SuggestedFollows'
|
import {SuggestedFollows} from '../discover/SuggestedFollows'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s} from '../../lib/styles'
|
import {s} from '../../lib/styles'
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,9 @@ import {
|
||||||
RepostedByViewItemModel,
|
RepostedByViewItemModel,
|
||||||
} from '../../../state/models/reposted-by-view'
|
} from '../../../state/models/reposted-by-view'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ export const PostRepostedBy = observer(function PostRepostedBy({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
PostThreadViewPostModel,
|
PostThreadViewPostModel,
|
||||||
} from '../../../state/models/post-thread-view'
|
} from '../../../state/models/post-thread-view'
|
||||||
import {PostThreadItem} from './PostThreadItem'
|
import {PostThreadItem} from './PostThreadItem'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
|
|
||||||
export const PostThread = observer(function PostThread({
|
export const PostThread = observer(function PostThread({
|
||||||
uri,
|
uri,
|
||||||
|
@ -57,7 +57,6 @@ export const PostThread = observer(function PostThread({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -7,9 +7,9 @@ import {AppBskyFeedPost} from '@atproto/api'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
|
import {PostThreadViewPostModel} from '../../../state/models/post-thread-view'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {RichText} from '../util/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {PostDropdownBtn} from '../util/DropdownBtn'
|
import {PostDropdownBtn} from '../util/forms/DropdownButton'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
|
@ -6,8 +6,8 @@ import {
|
||||||
VotesViewItemModel,
|
VotesViewItemModel,
|
||||||
} from '../../../state/models/votes-view'
|
} from '../../../state/models/votes-view'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
@ -57,7 +57,6 @@ export const PostVotedBy = observer(function PostVotedBy({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -17,8 +17,8 @@ import {UserInfoText} from '../util/UserInfoText'
|
||||||
import {PostMeta} from '../util/PostMeta'
|
import {PostMeta} from '../util/PostMeta'
|
||||||
import {PostEmbeds} from '../util/PostEmbeds'
|
import {PostEmbeds} from '../util/PostEmbeds'
|
||||||
import {PostCtrls} from '../util/PostCtrls'
|
import {PostCtrls} from '../util/PostCtrls'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {RichText} from '../util/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React, {useState, useEffect} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {PostModel} from '../../../state/models/post'
|
import {PostModel} from '../../../state/models/post'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,11 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {EmptyState} from '../util/EmptyState'
|
import {EmptyState} from '../util/EmptyState'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {FeedModel} from '../../../state/models/feed-view'
|
import {FeedModel} from '../../../state/models/feed-view'
|
||||||
import {FeedItem} from './FeedItem'
|
import {FeedItem} from './FeedItem'
|
||||||
import {ComposePrompt} from '../composer/Prompt'
|
import {ComposePrompt} from '../composer/Prompt'
|
||||||
import {OnScrollCb} from '../../lib/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
|
|
||||||
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
|
const COMPOSE_PROMPT_ITEM = {_reactKey: '__prompt__'}
|
||||||
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
const EMPTY_FEED_ITEM = {_reactKey: '__empty__'}
|
||||||
|
@ -80,7 +80,6 @@ export const Feed = observer(function Feed({
|
||||||
{feed.isLoading && !data && <PostFeedLoadingPlaceholder />}
|
{feed.isLoading && !data && <PostFeedLoadingPlaceholder />}
|
||||||
{feed.hasError && (
|
{feed.hasError && (
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={feed.error}
|
message={feed.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onPressTryAgain}
|
onPressTryAgain={onPressTryAgain}
|
||||||
|
|
|
@ -8,12 +8,12 @@ import {AppBskyFeedPost} from '@atproto/api'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {FeedItemModel} from '../../../state/models/feed-view'
|
import {FeedItemModel} from '../../../state/models/feed-view'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserInfoText} from '../util/UserInfoText'
|
import {UserInfoText} from '../util/UserInfoText'
|
||||||
import {PostMeta} from '../util/PostMeta'
|
import {PostMeta} from '../util/PostMeta'
|
||||||
import {PostCtrls} from '../util/PostCtrls'
|
import {PostCtrls} from '../util/PostCtrls'
|
||||||
import {PostEmbeds} from '../util/PostEmbeds'
|
import {PostEmbeds} from '../util/PostEmbeds'
|
||||||
import {RichText} from '../util/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import {
|
||||||
FollowerItem,
|
FollowerItem,
|
||||||
} from '../../../state/models/user-followers-view'
|
} from '../../../state/models/user-followers-view'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
@ -57,7 +57,6 @@ export const ProfileFollowers = observer(function ProfileFollowers({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import {
|
||||||
} from '../../../state/models/user-follows-view'
|
} from '../../../state/models/user-follows-view'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
|
||||||
|
@ -57,7 +57,6 @@ export const ProfileFollows = observer(function ProfileFollows({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -16,11 +16,11 @@ import {
|
||||||
import {pluralize} from '../../../lib/strings'
|
import {pluralize} from '../../../lib/strings'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {getGradient} from '../../lib/asset-gen'
|
import {getGradient} from '../../lib/asset-gen'
|
||||||
import {DropdownBtn, DropdownItem} from '../util/DropdownBtn'
|
import {DropdownButton, DropdownItem} from '../util/forms/DropdownButton'
|
||||||
import * as Toast from '../util/Toast'
|
import * as Toast from '../util/Toast'
|
||||||
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
import {LoadingPlaceholder} from '../util/LoadingPlaceholder'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from '../util/text/Text'
|
||||||
import {RichText} from '../util/RichText'
|
import {RichText} from '../util/text/RichText'
|
||||||
import {UserAvatar} from '../util/UserAvatar'
|
import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {UserBanner} from '../util/UserBanner'
|
import {UserBanner} from '../util/UserBanner'
|
||||||
import {UserInfoText} from '../util/UserInfoText'
|
import {UserInfoText} from '../util/UserInfoText'
|
||||||
|
@ -195,11 +195,11 @@ export const ProfileHeader = observer(function ProfileHeader({
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{dropdownItems?.length ? (
|
{dropdownItems?.length ? (
|
||||||
<DropdownBtn
|
<DropdownButton
|
||||||
items={dropdownItems}
|
items={dropdownItems}
|
||||||
style={[styles.btn, styles.secondaryBtn]}>
|
style={[styles.btn, styles.secondaryBtn]}>
|
||||||
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
|
<FontAwesomeIcon icon="ellipsis" style={[s.gray5]} />
|
||||||
</DropdownBtn>
|
</DropdownButton>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</View>
|
</View>
|
||||||
<View style={styles.displayNameLine}>
|
<View style={styles.displayNameLine}>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {observer} from 'mobx-react-lite'
|
||||||
import {ActivityIndicator, FlatList, View} from 'react-native'
|
import {ActivityIndicator, FlatList, View} from 'react-native'
|
||||||
import {MembersViewModel, MemberItem} from '../../../state/models/members-view'
|
import {MembersViewModel, MemberItem} from '../../../state/models/members-view'
|
||||||
import {ProfileCard} from './ProfileCard'
|
import {ProfileCard} from './ProfileCard'
|
||||||
import {ErrorMessage} from '../util/ErrorMessage'
|
import {ErrorMessage} from '../util/error/ErrorMessage'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
||||||
export const ProfileMembers = observer(function ProfileMembers({
|
export const ProfileMembers = observer(function ProfileMembers({
|
||||||
|
@ -49,7 +49,6 @@ export const ProfileMembers = observer(function ProfileMembers({
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={view.error}
|
message={view.error}
|
||||||
style={{margin: 6}}
|
style={{margin: 6}}
|
||||||
onPressTryAgain={onRefresh}
|
onPressTryAgain={onRefresh}
|
||||||
|
|
|
@ -2,9 +2,9 @@ import React from 'react'
|
||||||
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
|
||||||
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {UserGroupIcon} from '../../lib/icons'
|
import {UserGroupIcon} from '../../lib/icons'
|
||||||
import {colors} from '../../lib/styles'
|
import {usePalette} from '../../lib/hooks/usePalette'
|
||||||
|
|
||||||
export function EmptyState({
|
export function EmptyState({
|
||||||
icon,
|
icon,
|
||||||
|
@ -15,16 +15,23 @@ export function EmptyState({
|
||||||
message: string
|
message: string
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
const pal = usePalette('default')
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]}>
|
<View style={[styles.container, style]}>
|
||||||
<View style={styles.iconContainer}>
|
<View style={styles.iconContainer}>
|
||||||
{icon === 'user-group' ? (
|
{icon === 'user-group' ? (
|
||||||
<UserGroupIcon size="64" style={styles.icon} />
|
<UserGroupIcon size="64" style={styles.icon} />
|
||||||
) : (
|
) : (
|
||||||
<FontAwesomeIcon icon={icon} size={64} style={styles.icon} />
|
<FontAwesomeIcon
|
||||||
|
icon={icon}
|
||||||
|
size={64}
|
||||||
|
style={[styles.icon, pal.textLight]}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.text}>{message}</Text>
|
<Text type="body1" style={[pal.textLight, styles.text]}>
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -40,12 +47,9 @@ const styles = StyleSheet.create({
|
||||||
icon: {
|
icon: {
|
||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
color: colors.gray3,
|
|
||||||
},
|
},
|
||||||
text: {
|
text: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: colors.gray5,
|
|
||||||
paddingTop: 16,
|
paddingTop: 16,
|
||||||
fontSize: 16,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -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<ViewStyle>
|
|
||||||
onPressTryAgain?: () => void
|
|
||||||
}) {
|
|
||||||
const inner = (
|
|
||||||
<>
|
|
||||||
<View style={[styles.errorIcon, dark ? styles.darkErrorIcon : undefined]}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="exclamation"
|
|
||||||
style={{color: dark ? colors.red3 : colors.white}}
|
|
||||||
size={16}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
<Text
|
|
||||||
style={[styles.message, dark ? styles.darkMessage : undefined]}
|
|
||||||
numberOfLines={numberOfLines}>
|
|
||||||
{message}
|
|
||||||
</Text>
|
|
||||||
{onPressTryAgain && (
|
|
||||||
<TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
icon="arrows-rotate"
|
|
||||||
style={{color: dark ? colors.white : colors.red4}}
|
|
||||||
size={16}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
if (dark) {
|
|
||||||
return (
|
|
||||||
<LinearGradient
|
|
||||||
colors={[gradients.error.start, gradients.error.end]}
|
|
||||||
start={{x: 0.5, y: 0}}
|
|
||||||
end={{x: 1, y: 1}}
|
|
||||||
style={[styles.outer, style]}>
|
|
||||||
{inner}
|
|
||||||
</LinearGradient>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
return <View style={[styles.outer, style]}>{inner}</View>
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
})
|
|
|
@ -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 (
|
|
||||||
<TouchableWithoutFeedback onPress={onPress}>
|
|
||||||
<View style={styles.outer}>
|
|
||||||
<LinearGradient
|
|
||||||
colors={[gradients.purple.start, gradients.purple.end]}
|
|
||||||
start={{x: 0, y: 0}}
|
|
||||||
end={{x: 1, y: 1}}
|
|
||||||
style={styles.inner}>
|
|
||||||
<FontAwesomeIcon
|
|
||||||
size={24}
|
|
||||||
icon={icon}
|
|
||||||
color={colors.white}
|
|
||||||
style={styles.icon}
|
|
||||||
/>
|
|
||||||
</LinearGradient>
|
|
||||||
</View>
|
|
||||||
</TouchableWithoutFeedback>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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: {},
|
|
||||||
})
|
|
|
@ -9,7 +9,8 @@ import {
|
||||||
View,
|
View,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
|
import {TypographyVariant} from '../../lib/ThemeContext'
|
||||||
import {useStores, RootStoreModel} from '../../../state'
|
import {useStores, RootStoreModel} from '../../../state'
|
||||||
import {convertBskyAppUrlIfNeeded} from '../../../lib/strings'
|
import {convertBskyAppUrlIfNeeded} from '../../../lib/strings'
|
||||||
|
|
||||||
|
@ -57,14 +58,14 @@ export const Link = observer(function Link({
|
||||||
})
|
})
|
||||||
|
|
||||||
export const TextLink = observer(function Link({
|
export const TextLink = observer(function Link({
|
||||||
|
type = 'body1',
|
||||||
style,
|
style,
|
||||||
href,
|
href,
|
||||||
title,
|
|
||||||
text,
|
text,
|
||||||
}: {
|
}: {
|
||||||
|
type: TypographyVariant
|
||||||
style?: StyleProp<TextStyle>
|
style?: StyleProp<TextStyle>
|
||||||
href: string
|
href: string
|
||||||
title?: string
|
|
||||||
text: string
|
text: string
|
||||||
}) {
|
}) {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
@ -75,7 +76,7 @@ export const TextLink = observer(function Link({
|
||||||
handleLink(store, href, true)
|
handleLink(store, href, true)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={style} onPress={onPress} onLongPress={onLongPress}>
|
<Text type={type} style={style} onPress={onPress} onLongPress={onLongPress}>
|
||||||
{text}
|
{text}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {UpIcon} from '../../lib/icons'
|
import {UpIcon} from '../../lib/icons'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
import {useTheme} from '../../lib/ThemeContext'
|
||||||
|
|
||||||
export function LoadingPlaceholder({
|
export function LoadingPlaceholder({
|
||||||
width,
|
width,
|
||||||
|
@ -13,13 +14,14 @@ export function LoadingPlaceholder({
|
||||||
height: string | number
|
height: string | number
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
{
|
{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
backgroundColor: '#e7e9ea',
|
backgroundColor: theme.palette.default.backgroundLight,
|
||||||
borderRadius: 6,
|
borderRadius: 6,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
},
|
},
|
||||||
|
@ -29,7 +31,7 @@ export function LoadingPlaceholder({
|
||||||
style={{
|
style={{
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
backgroundColor: '#e7e9ea',
|
backgroundColor: theme.palette.default.backgroundLight,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
@ -41,6 +43,7 @@ export function PostLoadingPlaceholder({
|
||||||
}: {
|
}: {
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
<View style={[styles.post, style]}>
|
<View style={[styles.post, style]}>
|
||||||
<LoadingPlaceholder width={50} height={50} style={styles.avatar} />
|
<LoadingPlaceholder width={50} height={50} style={styles.avatar} />
|
||||||
|
@ -52,16 +55,24 @@ export function PostLoadingPlaceholder({
|
||||||
<View style={s.flexRow}>
|
<View style={s.flexRow}>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
style={s.gray3}
|
style={{color: theme.palette.default.icon}}
|
||||||
icon={['far', 'comment']}
|
icon={['far', 'comment']}
|
||||||
size={14}
|
size={14}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<FontAwesomeIcon style={s.gray3} icon="retweet" size={18} />
|
<FontAwesomeIcon
|
||||||
|
style={{color: theme.palette.default.icon}}
|
||||||
|
icon="retweet"
|
||||||
|
size={18}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.flex1}>
|
<View style={s.flex1}>
|
||||||
<UpIcon style={s.gray3} size={17} strokeWidth={1.7} />
|
<UpIcon
|
||||||
|
style={{color: theme.palette.default.icon}}
|
||||||
|
size={17}
|
||||||
|
strokeWidth={1.7}
|
||||||
|
/>
|
||||||
</View>
|
</View>
|
||||||
<View style={s.flex1}></View>
|
<View style={s.flex1}></View>
|
||||||
</View>
|
</View>
|
||||||
|
@ -125,8 +136,6 @@ export function NotificationFeedLoadingPlaceholder() {
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
post: {
|
post: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
backgroundColor: colors.white,
|
|
||||||
borderRadius: 6,
|
|
||||||
padding: 10,
|
padding: 10,
|
||||||
margin: 1,
|
margin: 1,
|
||||||
},
|
},
|
||||||
|
@ -135,8 +144,6 @@ const styles = StyleSheet.create({
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
},
|
},
|
||||||
notification: {
|
notification: {
|
||||||
backgroundColor: colors.white,
|
|
||||||
borderRadius: 6,
|
|
||||||
padding: 10,
|
padding: 10,
|
||||||
paddingLeft: 46,
|
paddingLeft: 46,
|
||||||
margin: 1,
|
margin: 1,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// TODO: replaceme with something in the design system
|
||||||
|
|
||||||
import React, {useRef} from 'react'
|
import React, {useRef} from 'react'
|
||||||
import {
|
import {
|
||||||
StyleProp,
|
StyleProp,
|
||||||
|
@ -13,7 +15,7 @@ import {
|
||||||
FontAwesomeIconStyle,
|
FontAwesomeIconStyle,
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
import RootSiblings from 'react-native-root-siblings'
|
import RootSiblings from 'react-native-root-siblings'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../lib/styles'
|
||||||
|
|
||||||
interface PickerItem {
|
interface PickerItem {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import React from 'react'
|
||||||
import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {Animated, StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'
|
import ReactNativeHapticFeedback from 'react-native-haptic-feedback'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {UpIcon, UpIconSolid} from '../../lib/icons'
|
import {UpIcon, UpIconSolid} from '../../lib/icons'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
interface PostCtrlsOpts {
|
interface PostCtrlsOpts {
|
||||||
big?: boolean
|
big?: boolean
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react'
|
||||||
import {ImageStyle, StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
|
import {ImageStyle, StyleSheet, StyleProp, View, ViewStyle} from 'react-native'
|
||||||
import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api'
|
import {AppBskyEmbedImages, AppBskyEmbedExternal} from '@atproto/api'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from './text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../lib/styles'
|
||||||
import {AutoSizedImage} from './images/AutoSizedImage'
|
import {AutoSizedImage} from './images/AutoSizedImage'
|
||||||
import {ImagesLightbox} from '../../../state/models/shell-ui'
|
import {ImagesLightbox} from '../../../state/models/shell-ui'
|
||||||
|
|
|
@ -2,8 +2,8 @@ import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Link} from '../util/Link'
|
import {Link} from '../util/Link'
|
||||||
import {Text} from '../util/Text'
|
import {Text} from './text/Text'
|
||||||
import {PostDropdownBtn} from '../util/DropdownBtn'
|
import {PostDropdownBtn} from './forms/DropdownButton'
|
||||||
import {s} from '../../lib/styles'
|
import {s} from '../../lib/styles'
|
||||||
import {ago} from '../../../lib/strings'
|
import {ago} from '../../../lib/strings'
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
TouchableWithoutFeedback,
|
TouchableWithoutFeedback,
|
||||||
View,
|
View,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../lib/styles'
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
|
|
|
@ -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<TextProps>) {
|
|
||||||
return (
|
|
||||||
<RNText style={[s.black, style]} {...props}>
|
|
||||||
{children}
|
|
||||||
</RNText>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react'
|
||||||
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
|
import {AppBskyActorGetProfile as GetProfile} from '@atproto/api'
|
||||||
import {StyleProp, TextStyle} from 'react-native'
|
import {StyleProp, TextStyle} from 'react-native'
|
||||||
import {Link} from './Link'
|
import {Link} from './Link'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
import {LoadingPlaceholder} from './LoadingPlaceholder'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {UserAvatar} from './UserAvatar'
|
import {UserAvatar} from './UserAvatar'
|
||||||
import {Text} from './Text'
|
import {Text} from './text/Text'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {MagnifyingGlassIcon} from '../../lib/icons'
|
import {MagnifyingGlassIcon} from '../../lib/icons'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
|
|
|
@ -7,8 +7,8 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {Selector} from './Selector'
|
import {Selector} from './Selector'
|
||||||
import {HorzSwipe} from './gestures/HorzSwipe'
|
import {HorzSwipe} from './gestures/HorzSwipe'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
import {OnScrollCb} from '../../lib/useOnMainScroll'
|
import {OnScrollCb} from '../../lib/hooks/useOnMainScroll'
|
||||||
|
|
||||||
const HEADER_ITEM = {_reactKey: '__header__'}
|
const HEADER_ITEM = {_reactKey: '__header__'}
|
||||||
const SELECTOR_ITEM = {_reactKey: '__selector__'}
|
const SELECTOR_ITEM = {_reactKey: '__selector__'}
|
||||||
|
|
|
@ -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<ViewStyle>
|
||||||
|
onPressTryAgain?: () => void
|
||||||
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const pal = usePalette('error')
|
||||||
|
return (
|
||||||
|
<View style={[styles.outer, pal.view, style]}>
|
||||||
|
<View
|
||||||
|
style={[styles.errorIcon, {backgroundColor: theme.palette.error.icon}]}>
|
||||||
|
<FontAwesomeIcon icon="exclamation" style={pal.text} size={16} />
|
||||||
|
</View>
|
||||||
|
<Text
|
||||||
|
type="body2"
|
||||||
|
style={[styles.message, pal.text]}
|
||||||
|
numberOfLines={numberOfLines}>
|
||||||
|
{message}
|
||||||
|
</Text>
|
||||||
|
{onPressTryAgain && (
|
||||||
|
<TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="arrows-rotate"
|
||||||
|
style={{color: theme.palette.error.icon}}
|
||||||
|
size={18}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
})
|
|
@ -1,8 +1,10 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Text} from './Text'
|
import {Text} from '../text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {colors} from '../../../lib/styles'
|
||||||
|
import {useTheme} from '../../../lib/ThemeContext'
|
||||||
|
import {usePalette} from '../../../lib/hooks/usePalette'
|
||||||
|
|
||||||
export function ErrorScreen({
|
export function ErrorScreen({
|
||||||
title,
|
title,
|
||||||
|
@ -15,10 +17,16 @@ export function ErrorScreen({
|
||||||
details?: string
|
details?: string
|
||||||
onPressTryAgain?: () => void
|
onPressTryAgain?: () => void
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const pal = usePalette('error')
|
||||||
return (
|
return (
|
||||||
<View style={styles.outer}>
|
<View style={[styles.outer, pal.view]}>
|
||||||
<View style={styles.errorIconContainer}>
|
<View style={styles.errorIconContainer}>
|
||||||
<View style={styles.errorIcon}>
|
<View
|
||||||
|
style={[
|
||||||
|
styles.errorIcon,
|
||||||
|
{backgroundColor: theme.palette.error.icon},
|
||||||
|
]}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon="exclamation"
|
icon="exclamation"
|
||||||
style={{color: colors.white}}
|
style={{color: colors.white}}
|
||||||
|
@ -26,18 +34,30 @@ export function ErrorScreen({
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.title}>{title}</Text>
|
<Text type="h3" style={[styles.title, pal.text]}>
|
||||||
<Text style={styles.message}>{message}</Text>
|
{title}
|
||||||
{details && <Text style={styles.details}>{details}</Text>}
|
</Text>
|
||||||
|
<Text style={[styles.message, pal.textLight]}>{message}</Text>
|
||||||
|
{details && (
|
||||||
|
<Text
|
||||||
|
type="body2"
|
||||||
|
style={[
|
||||||
|
styles.details,
|
||||||
|
pal.textInverted,
|
||||||
|
{backgroundColor: theme.palette.default.background},
|
||||||
|
]}>
|
||||||
|
{details}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
{onPressTryAgain && (
|
{onPressTryAgain && (
|
||||||
<View style={styles.btnContainer}>
|
<View style={styles.btnContainer}>
|
||||||
<TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
|
<TouchableOpacity
|
||||||
<FontAwesomeIcon
|
style={[styles.btn, {backgroundColor: theme.palette.error.icon}]}
|
||||||
icon="arrows-rotate"
|
onPress={onPressTryAgain}>
|
||||||
style={{color: colors.white}}
|
<FontAwesomeIcon icon="arrows-rotate" style={pal.text} size={16} />
|
||||||
size={16}
|
<Text type="button" style={[styles.btnText, pal.text]}>
|
||||||
/>
|
Try again
|
||||||
<Text style={styles.btnText}>Try again</Text>
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
@ -48,32 +68,19 @@ export function ErrorScreen({
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
outer: {
|
outer: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
backgroundColor: colors.red1,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.red3,
|
|
||||||
borderRadius: 6,
|
|
||||||
paddingVertical: 30,
|
paddingVertical: 30,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
margin: 10,
|
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: colors.red4,
|
|
||||||
fontSize: 24,
|
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
message: {
|
message: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: colors.red4,
|
|
||||||
marginBottom: 20,
|
marginBottom: 20,
|
||||||
},
|
},
|
||||||
details: {
|
details: {
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
color: colors.black,
|
|
||||||
backgroundColor: colors.white,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.gray5,
|
|
||||||
borderRadius: 6,
|
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
paddingHorizontal: 14,
|
paddingHorizontal: 14,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
|
@ -85,23 +92,17 @@ const styles = StyleSheet.create({
|
||||||
btn: {
|
btn: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: colors.red4,
|
|
||||||
borderRadius: 6,
|
|
||||||
paddingHorizontal: 16,
|
paddingHorizontal: 16,
|
||||||
paddingVertical: 10,
|
paddingVertical: 10,
|
||||||
},
|
},
|
||||||
btnText: {
|
btnText: {
|
||||||
marginLeft: 5,
|
marginLeft: 5,
|
||||||
color: colors.white,
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: 'bold',
|
|
||||||
},
|
},
|
||||||
errorIconContainer: {
|
errorIconContainer: {
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 10,
|
marginBottom: 10,
|
||||||
},
|
},
|
||||||
errorIcon: {
|
errorIcon: {
|
||||||
backgroundColor: colors.red4,
|
|
||||||
borderRadius: 30,
|
borderRadius: 30,
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
|
@ -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<ViewStyle>
|
||||||
|
onPress?: () => void
|
||||||
|
}>) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const outerStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>(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<TextStyle, Record<ButtonType, TextStyle>>(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 (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[outerStyle, styles.outer, style]}
|
||||||
|
onPress={onPress}>
|
||||||
|
{label ? (
|
||||||
|
<Text type="button" style={[labelStyle]}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
) : (
|
||||||
|
children
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
outer: {
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 8,
|
||||||
|
},
|
||||||
|
})
|
|
@ -11,12 +11,13 @@ import {
|
||||||
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
import {IconProp} from '@fortawesome/fontawesome-svg-core'
|
||||||
import RootSiblings from 'react-native-root-siblings'
|
import RootSiblings from 'react-native-root-siblings'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {Text} from './Text'
|
import {Text} from '../text/Text'
|
||||||
import {colors} from '../../lib/styles'
|
import {Button, ButtonType} from './Button'
|
||||||
import {toShareUrl} from '../../../lib/strings'
|
import {colors} from '../../../lib/styles'
|
||||||
import {useStores} from '../../../state'
|
import {toShareUrl} from '../../../../lib/strings'
|
||||||
import {ReportPostModal, ConfirmModal} from '../../../state/models/shell-ui'
|
import {useStores} from '../../../../state'
|
||||||
import {TABS_ENABLED} from '../../../build-flags'
|
import {ReportPostModal, ConfirmModal} from '../../../../state/models/shell-ui'
|
||||||
|
import {TABS_ENABLED} from '../../../../build-flags'
|
||||||
|
|
||||||
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
const HITSLOP = {left: 10, top: 10, right: 10, bottom: 10}
|
||||||
|
|
||||||
|
@ -26,14 +27,20 @@ export interface DropdownItem {
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DropdownBtn({
|
export type DropdownButtonType = ButtonType | 'bare'
|
||||||
|
|
||||||
|
export function DropdownButton({
|
||||||
|
type = 'bare',
|
||||||
style,
|
style,
|
||||||
items,
|
items,
|
||||||
|
label,
|
||||||
menuWidth,
|
menuWidth,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
|
type: DropdownButtonType
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
items: DropdownItem[]
|
items: DropdownItem[]
|
||||||
|
label?: string
|
||||||
menuWidth?: number
|
menuWidth?: number
|
||||||
children?: React.ReactNode
|
children?: React.ReactNode
|
||||||
}) {
|
}) {
|
||||||
|
@ -62,14 +69,23 @@ export function DropdownBtn({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === 'bare') {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={style}
|
||||||
|
onPress={onPress}
|
||||||
|
hitSlop={HITSLOP}
|
||||||
|
ref={ref}>
|
||||||
|
{children}
|
||||||
|
</TouchableOpacity>
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<View ref={ref}>
|
||||||
style={style}
|
<Button onPress={onPress} style={style} label={label}>
|
||||||
onPress={onPress}
|
{children}
|
||||||
hitSlop={HITSLOP}
|
</Button>
|
||||||
ref={ref}>
|
</View>
|
||||||
{children}
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +93,6 @@ export function PostDropdownBtn({
|
||||||
style,
|
style,
|
||||||
children,
|
children,
|
||||||
itemHref,
|
itemHref,
|
||||||
itemTitle,
|
|
||||||
isAuthor,
|
isAuthor,
|
||||||
onCopyPostText,
|
onCopyPostText,
|
||||||
onDeletePost,
|
onDeletePost,
|
||||||
|
@ -141,9 +156,9 @@ export function PostDropdownBtn({
|
||||||
].filter(Boolean) as DropdownItem[]
|
].filter(Boolean) as DropdownItem[]
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownBtn style={style} items={dropdownItems} menuWidth={200}>
|
<DropdownButton style={style} items={dropdownItems} menuWidth={200}>
|
||||||
{children}
|
{children}
|
||||||
</DropdownBtn>
|
</DropdownButton>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,126 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleProp, StyleSheet, TextStyle, View, ViewStyle} from 'react-native'
|
||||||
import {Text} from '../Text'
|
import {Text} from '../text/Text'
|
||||||
import {colors} from '../../../lib/styles'
|
import {Button, ButtonType} from './Button'
|
||||||
|
import {useTheme} from '../../../lib/ThemeContext'
|
||||||
|
import {choose} from '../../../../lib/functions'
|
||||||
|
|
||||||
export function RadioButton({
|
export function RadioButton({
|
||||||
|
type = 'default-light',
|
||||||
label,
|
label,
|
||||||
isSelected,
|
isSelected,
|
||||||
|
style,
|
||||||
onPress,
|
onPress,
|
||||||
}: {
|
}: {
|
||||||
|
type?: ButtonType
|
||||||
label: string
|
label: string
|
||||||
isSelected: boolean
|
isSelected: boolean
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
onPress: () => void
|
onPress: () => void
|
||||||
}) {
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const circleStyle = choose<TextStyle, Record<ButtonType, TextStyle>>(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<TextStyle, Record<ButtonType, TextStyle>>(
|
||||||
|
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<TextStyle, Record<ButtonType, TextStyle>>(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 (
|
return (
|
||||||
<TouchableOpacity style={styles.outer} onPress={onPress}>
|
<Button type={type} onPress={onPress} style={style}>
|
||||||
<View style={styles.circle}>
|
<View style={styles.outer}>
|
||||||
{isSelected ? <View style={styles.circleFill} /> : undefined}
|
<View style={[circleStyle, styles.circle]}>
|
||||||
|
{isSelected ? (
|
||||||
|
<View style={[circleFillStyle, styles.circleFill]} />
|
||||||
|
) : undefined}
|
||||||
|
</View>
|
||||||
|
<Text type="button" style={[labelStyle, styles.label]}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text style={styles.label}>{label}</Text>
|
</Button>
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,30 +128,21 @@ const styles = StyleSheet.create({
|
||||||
outer: {
|
outer: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
marginBottom: 5,
|
|
||||||
borderRadius: 8,
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: colors.gray2,
|
|
||||||
paddingHorizontal: 10,
|
|
||||||
paddingVertical: 8,
|
|
||||||
},
|
},
|
||||||
circle: {
|
circle: {
|
||||||
width: 30,
|
width: 26,
|
||||||
height: 30,
|
height: 26,
|
||||||
borderRadius: 15,
|
borderRadius: 15,
|
||||||
padding: 4,
|
padding: 4,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: colors.gray3,
|
|
||||||
marginRight: 10,
|
marginRight: 10,
|
||||||
},
|
},
|
||||||
circleFill: {
|
circleFill: {
|
||||||
width: 20,
|
width: 16,
|
||||||
height: 20,
|
height: 16,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
backgroundColor: colors.blue3,
|
|
||||||
},
|
},
|
||||||
label: {
|
label: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
fontSize: 17,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import React, {useState} from 'react'
|
import React, {useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {RadioButton} from './RadioButton'
|
import {RadioButton} from './RadioButton'
|
||||||
|
import {ButtonType} from './Button'
|
||||||
|
|
||||||
export interface RadioGroupItem {
|
export interface RadioGroupItem {
|
||||||
label: string
|
label: string
|
||||||
|
@ -8,22 +9,28 @@ export interface RadioGroupItem {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RadioGroup({
|
export function RadioGroup({
|
||||||
|
type,
|
||||||
items,
|
items,
|
||||||
|
initialSelection = '',
|
||||||
onSelect,
|
onSelect,
|
||||||
}: {
|
}: {
|
||||||
|
type?: ButtonType
|
||||||
items: RadioGroupItem[]
|
items: RadioGroupItem[]
|
||||||
|
initialSelection?: string
|
||||||
onSelect: (key: string) => void
|
onSelect: (key: string) => void
|
||||||
}) {
|
}) {
|
||||||
const [selection, setSelection] = useState<string>('')
|
const [selection, setSelection] = useState<string>(initialSelection)
|
||||||
const onSelectInner = (key: string) => {
|
const onSelectInner = (key: string) => {
|
||||||
setSelection(key)
|
setSelection(key)
|
||||||
onSelect(key)
|
onSelect(key)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{items.map(item => (
|
{items.map((item, i) => (
|
||||||
<RadioButton
|
<RadioButton
|
||||||
key={item.key}
|
key={item.key}
|
||||||
|
style={i !== 0 ? {marginTop: 2} : undefined}
|
||||||
|
type={type}
|
||||||
label={item.label}
|
label={item.label}
|
||||||
isSelected={item.key === selection}
|
isSelected={item.key === selection}
|
||||||
onPress={() => onSelectInner(item.key)}
|
onPress={() => onSelectInner(item.key)}
|
||||||
|
|
|
@ -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<ViewStyle>
|
||||||
|
onPress: () => void
|
||||||
|
}) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const circleStyle = choose<TextStyle, Record<ButtonType, TextStyle>>(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<TextStyle, Record<ButtonType, TextStyle>>(
|
||||||
|
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<TextStyle, Record<ButtonType, TextStyle>>(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 (
|
||||||
|
<Button type={type} onPress={onPress} style={style}>
|
||||||
|
<View style={styles.outer}>
|
||||||
|
<View style={[circleStyle, styles.circle]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
circleFillStyle,
|
||||||
|
styles.circleFill,
|
||||||
|
isSelected ? styles.circleFillSelected : undefined,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text type="button" style={[labelStyle, styles.label]}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
})
|
|
@ -9,7 +9,7 @@ import {
|
||||||
View,
|
View,
|
||||||
ViewStyle,
|
ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {Text} from '../Text'
|
import {Text} from '../text/Text'
|
||||||
import {colors} from '../../../lib/styles'
|
import {colors} from '../../../lib/styles'
|
||||||
|
|
||||||
const MAX_HEIGHT = 300
|
const MAX_HEIGHT = 300
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {TextStyle, StyleProp} from 'react-native'
|
import {TextStyle, StyleProp} from 'react-native'
|
||||||
import {TextLink} from './Link'
|
import {TextLink} from '../Link'
|
||||||
import {Text} from './Text'
|
import {Text} from './Text'
|
||||||
import {s} from '../../lib/styles'
|
import {s} from '../../../lib/styles'
|
||||||
import {toShortUrl} from '../../../lib/strings'
|
import {toShortUrl} from '../../../../lib/strings'
|
||||||
|
import {TypographyVariant} from '../../../lib/ThemeContext'
|
||||||
|
import {usePalette} from '../../../lib/hooks/usePalette'
|
||||||
|
|
||||||
type TextSlice = {start: number; end: number}
|
type TextSlice = {start: number; end: number}
|
||||||
type Entity = {
|
type Entity = {
|
||||||
|
@ -13,16 +15,19 @@ type Entity = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RichText({
|
export function RichText({
|
||||||
|
type = 'body1',
|
||||||
text,
|
text,
|
||||||
entities,
|
entities,
|
||||||
style,
|
style,
|
||||||
numberOfLines,
|
numberOfLines,
|
||||||
}: {
|
}: {
|
||||||
|
type: TypographyVariant
|
||||||
text: string
|
text: string
|
||||||
entities?: Entity[]
|
entities?: Entity[]
|
||||||
style?: StyleProp<TextStyle>
|
style?: StyleProp<TextStyle>
|
||||||
numberOfLines?: number
|
numberOfLines?: number
|
||||||
}) {
|
}) {
|
||||||
|
const pal = usePalette('default')
|
||||||
if (!entities?.length) {
|
if (!entities?.length) {
|
||||||
if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) {
|
if (/^\p{Extended_Pictographic}+$/u.test(text) && text.length <= 5) {
|
||||||
style = {
|
style = {
|
||||||
|
@ -47,18 +52,20 @@ export function RichText({
|
||||||
els.push(
|
els.push(
|
||||||
<TextLink
|
<TextLink
|
||||||
key={key}
|
key={key}
|
||||||
|
type={type}
|
||||||
text={segment.text}
|
text={segment.text}
|
||||||
href={`/profile/${segment.entity.value}`}
|
href={`/profile/${segment.entity.value}`}
|
||||||
style={[style, s.blue3]}
|
style={[style, pal.link]}
|
||||||
/>,
|
/>,
|
||||||
)
|
)
|
||||||
} else if (segment.entity.type === 'link') {
|
} else if (segment.entity.type === 'link') {
|
||||||
els.push(
|
els.push(
|
||||||
<TextLink
|
<TextLink
|
||||||
key={key}
|
key={key}
|
||||||
|
type={type}
|
||||||
text={toShortUrl(segment.text)}
|
text={toShortUrl(segment.text)}
|
||||||
href={segment.entity.value}
|
href={segment.entity.value}
|
||||||
style={[style, s.blue3]}
|
style={[style, pal.link]}
|
||||||
/>,
|
/>,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +73,7 @@ export function RichText({
|
||||||
key++
|
key++
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Text style={style} numberOfLines={numberOfLines}>
|
<Text type={type} style={[pal.text, style]} numberOfLines={numberOfLines}>
|
||||||
{els}
|
{els}
|
||||||
</Text>
|
</Text>
|
||||||
)
|
)
|
|
@ -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<CustomTextProps>) {
|
||||||
|
const theme = useTheme()
|
||||||
|
const typography = theme.typography[type]
|
||||||
|
return (
|
||||||
|
<RNText style={[s.black, typography, style]} {...props}>
|
||||||
|
{children}
|
||||||
|
</RNText>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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<PaletteColorName, PaletteColor>
|
||||||
|
|
||||||
|
export type ShapeName = 'button' | 'bigButton' | 'smallButton'
|
||||||
|
export type Shapes = Record<ShapeName, ViewStyle>
|
||||||
|
|
||||||
|
export type TypographyVariant =
|
||||||
|
| 'h1'
|
||||||
|
| 'h2'
|
||||||
|
| 'h3'
|
||||||
|
| 'h4'
|
||||||
|
| 'subtitle1'
|
||||||
|
| 'subtitle2'
|
||||||
|
| 'body1'
|
||||||
|
| 'body2'
|
||||||
|
| 'button'
|
||||||
|
| 'caption'
|
||||||
|
| 'overline'
|
||||||
|
export type Typography = Record<TypographyVariant, TextStyle>
|
||||||
|
|
||||||
|
export interface Theme {
|
||||||
|
colorScheme: ColorScheme
|
||||||
|
palette: Palette
|
||||||
|
shapes: Shapes
|
||||||
|
typography: Typography
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ThemeProviderProps {
|
||||||
|
theme?: ColorScheme
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ThemeContext = createContext<Theme>(defaultTheme)
|
||||||
|
|
||||||
|
export const useTheme = () => useContext(ThemeContext)
|
||||||
|
|
||||||
|
export const ThemeProvider: React.FC<ThemeProviderProps> = ({
|
||||||
|
theme,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const colorScheme = useColorScheme()
|
||||||
|
|
||||||
|
const value = useMemo(
|
||||||
|
() => ((theme || colorScheme) === 'dark' ? darkTheme : defaultTheme),
|
||||||
|
[colorScheme, theme],
|
||||||
|
)
|
||||||
|
|
||||||
|
return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import {useState} from 'react'
|
import {useState} from 'react'
|
||||||
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
|
import {NativeSyntheticEvent, NativeScrollEvent} from 'react-native'
|
||||||
import {RootStoreModel} from '../../state'
|
import {RootStoreModel} from '../../../state'
|
||||||
|
|
||||||
export type OnScrollCb = (
|
export type OnScrollCb = (
|
||||||
event: NativeSyntheticEvent<NativeScrollEvent>,
|
event: NativeSyntheticEvent<NativeScrollEvent>,
|
|
@ -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,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
export const FAB = 1
|
|
||||||
export const BASE = 0
|
|
|
@ -15,6 +15,7 @@ import {ProfileFollowers} from './screens/ProfileFollowers'
|
||||||
import {ProfileFollows} from './screens/ProfileFollows'
|
import {ProfileFollows} from './screens/ProfileFollows'
|
||||||
import {ProfileMembers} from './screens/ProfileMembers'
|
import {ProfileMembers} from './screens/ProfileMembers'
|
||||||
import {Settings} from './screens/Settings'
|
import {Settings} from './screens/Settings'
|
||||||
|
import {Debug} from './screens/Debug'
|
||||||
|
|
||||||
export type ScreenParams = {
|
export type ScreenParams = {
|
||||||
navIdx: [number, number]
|
navIdx: [number, number]
|
||||||
|
@ -71,6 +72,7 @@ export const routes: Route[] = [
|
||||||
'retweet',
|
'retweet',
|
||||||
r('/profile/(?<name>[^/]+)/post/(?<rkey>[^/]+)/reposted-by'),
|
r('/profile/(?<name>[^/]+)/post/(?<rkey>[^/]+)/reposted-by'),
|
||||||
],
|
],
|
||||||
|
[Debug, 'Debug', 'house', r('/debug')],
|
||||||
]
|
]
|
||||||
|
|
||||||
export function match(url: string): MatchResult {
|
export function match(url: string): MatchResult {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import {StyleSheet, TextInput, View} from 'react-native'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
|
import {ProfileFollows as ProfileFollowsComponent} from '../com/profile/ProfileFollows'
|
||||||
import {Selector} from '../com/util/Selector'
|
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 {colors} from '../lib/styles'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {useAnimatedValue} from '../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
|
export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
|
|
@ -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 (
|
||||||
|
<ThemeProvider theme={colorScheme}>
|
||||||
|
<DebugInner
|
||||||
|
colorScheme={colorScheme}
|
||||||
|
onToggleColorScheme={onToggleColorScheme}
|
||||||
|
/>
|
||||||
|
</ThemeProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DebugInner({
|
||||||
|
colorScheme,
|
||||||
|
onToggleColorScheme,
|
||||||
|
}: {
|
||||||
|
colorScheme: 'light' | 'dark'
|
||||||
|
onToggleColorScheme: () => void
|
||||||
|
}) {
|
||||||
|
const [currentView, setCurrentView] = React.useState<number>(0)
|
||||||
|
const pal = usePalette('default')
|
||||||
|
|
||||||
|
const renderItem = item => {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<View style={{paddingTop: 10, paddingHorizontal: 10}}>
|
||||||
|
<ToggleButton
|
||||||
|
type="default-light"
|
||||||
|
onPress={onToggleColorScheme}
|
||||||
|
isSelected={colorScheme === 'dark'}
|
||||||
|
label="Dark mode"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
{item.currentView === 2 ? (
|
||||||
|
<ErrorView key="error" />
|
||||||
|
) : item.currentView === 1 ? (
|
||||||
|
<ControlsView key="controls" />
|
||||||
|
) : (
|
||||||
|
<BaseView key="base" />
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const items = [{currentView}]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[{flex: 1}, pal.view]}>
|
||||||
|
<ViewHeader title="Debug panel" />
|
||||||
|
<ViewSelector
|
||||||
|
swipeEnabled
|
||||||
|
sections={MAIN_VIEWS}
|
||||||
|
items={items}
|
||||||
|
renderItem={renderItem}
|
||||||
|
onSelectView={setCurrentView}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function Heading({label}: {label: string}) {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
return (
|
||||||
|
<View style={{paddingTop: 10, paddingBottom: 5}}>
|
||||||
|
<Text type="h3" style={pal.text}>
|
||||||
|
{label}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function BaseView() {
|
||||||
|
return (
|
||||||
|
<View style={{paddingHorizontal: 10}}>
|
||||||
|
<Heading label="Palettes" />
|
||||||
|
<PaletteView palette="default" />
|
||||||
|
<PaletteView palette="primary" />
|
||||||
|
<PaletteView palette="secondary" />
|
||||||
|
<PaletteView palette="inverted" />
|
||||||
|
<PaletteView palette="error" />
|
||||||
|
<Heading label="Typography" />
|
||||||
|
<TypographyView />
|
||||||
|
<Heading label="Empty state" />
|
||||||
|
<EmptyStateView />
|
||||||
|
<Heading label="Loading placeholders" />
|
||||||
|
<LoadingPlaceholderView />
|
||||||
|
<View style={{height: 200}} />
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ControlsView() {
|
||||||
|
return (
|
||||||
|
<ScrollView style={{paddingHorizontal: 10}}>
|
||||||
|
<Heading label="Buttons" />
|
||||||
|
<ButtonsView />
|
||||||
|
<Heading label="Dropdown Buttons" />
|
||||||
|
<DropdownButtonsView />
|
||||||
|
<Heading label="Toggle Buttons" />
|
||||||
|
<ToggleButtonsView />
|
||||||
|
<Heading label="Radio Buttons" />
|
||||||
|
<RadioButtonsView />
|
||||||
|
<View style={{height: 200}} />
|
||||||
|
</ScrollView>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ErrorView() {
|
||||||
|
return (
|
||||||
|
<View style={{padding: 10}}>
|
||||||
|
<View style={{marginBottom: 5}}>
|
||||||
|
<ErrorScreen
|
||||||
|
title="Error screen"
|
||||||
|
message="A major error occurred that led the entire screen to fail"
|
||||||
|
details="Here are some details"
|
||||||
|
onPressTryAgain={() => {}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{marginBottom: 5}}>
|
||||||
|
<ErrorMessage message="This is an error that occurred while things were being done" />
|
||||||
|
</View>
|
||||||
|
<View style={{marginBottom: 5}}>
|
||||||
|
<ErrorMessage
|
||||||
|
message="This is an error that occurred while things were being done"
|
||||||
|
numberOfLines={1}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{marginBottom: 5}}>
|
||||||
|
<ErrorMessage
|
||||||
|
message="This is an error that occurred while things were being done"
|
||||||
|
onPressTryAgain={() => {}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{marginBottom: 5}}>
|
||||||
|
<ErrorMessage
|
||||||
|
message="This is an error that occurred while things were being done"
|
||||||
|
onPressTryAgain={() => {}}
|
||||||
|
numberOfLines={1}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function PaletteView({palette}: {palette: PaletteColorName}) {
|
||||||
|
const defaultPal = usePalette('default')
|
||||||
|
const pal = usePalette(palette)
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
pal.view,
|
||||||
|
pal.border,
|
||||||
|
{
|
||||||
|
padding: 10,
|
||||||
|
marginBottom: 5,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Text style={[pal.text]}>{palette} colors</Text>
|
||||||
|
<Text style={[pal.textLight]}>Light text</Text>
|
||||||
|
<Text style={[pal.link]}>Link text</Text>
|
||||||
|
{palette !== 'default' && (
|
||||||
|
<View style={[defaultPal.view]}>
|
||||||
|
<Text style={[pal.textInverted]}>Inverted text</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function TypographyView() {
|
||||||
|
const pal = usePalette('default')
|
||||||
|
return (
|
||||||
|
<View style={[pal.view]}>
|
||||||
|
<Text type="h1" style={[pal.text]}>
|
||||||
|
Heading 1
|
||||||
|
</Text>
|
||||||
|
<Text type="h2" style={[pal.text]}>
|
||||||
|
Heading 2
|
||||||
|
</Text>
|
||||||
|
<Text type="h3" style={[pal.text]}>
|
||||||
|
Heading 3
|
||||||
|
</Text>
|
||||||
|
<Text type="h4" style={[pal.text]}>
|
||||||
|
Heading 4
|
||||||
|
</Text>
|
||||||
|
<Text type="subtitle1" style={[pal.text]}>
|
||||||
|
Subtitle 1
|
||||||
|
</Text>
|
||||||
|
<Text type="subtitle2" style={[pal.text]}>
|
||||||
|
Subtitle 2
|
||||||
|
</Text>
|
||||||
|
<Text type="body1" style={[pal.text]}>
|
||||||
|
Body 1
|
||||||
|
</Text>
|
||||||
|
<Text type="body2" style={[pal.text]}>
|
||||||
|
Body 2
|
||||||
|
</Text>
|
||||||
|
<Text type="button" style={[pal.text]}>
|
||||||
|
Button
|
||||||
|
</Text>
|
||||||
|
<Text type="caption" style={[pal.text]}>
|
||||||
|
Caption
|
||||||
|
</Text>
|
||||||
|
<Text type="overline" style={[pal.text]}>
|
||||||
|
Overline
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function EmptyStateView() {
|
||||||
|
return <EmptyState icon="bars" message="This is an empty state" />
|
||||||
|
}
|
||||||
|
|
||||||
|
function LoadingPlaceholderView() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LoadingPlaceholder.PostLoadingPlaceholder />
|
||||||
|
<LoadingPlaceholder.NotificationLoadingPlaceholder />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ButtonsView() {
|
||||||
|
const defaultPal = usePalette('default')
|
||||||
|
const buttonStyles = {marginRight: 5}
|
||||||
|
return (
|
||||||
|
<View style={[defaultPal.view]}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginBottom: 5,
|
||||||
|
}}>
|
||||||
|
<Button type="primary" label="Primary solid" style={buttonStyles} />
|
||||||
|
<Button type="secondary" label="Secondary solid" style={buttonStyles} />
|
||||||
|
<Button type="inverted" label="Inverted solid" style={buttonStyles} />
|
||||||
|
</View>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<Button
|
||||||
|
type="primary-outline"
|
||||||
|
label="Primary outline"
|
||||||
|
style={buttonStyles}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="secondary-outline"
|
||||||
|
label="Secondary outline"
|
||||||
|
style={buttonStyles}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<Button
|
||||||
|
type="primary-light"
|
||||||
|
label="Primary light"
|
||||||
|
style={buttonStyles}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="secondary-light"
|
||||||
|
label="Secondary light"
|
||||||
|
style={buttonStyles}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<Button
|
||||||
|
type="default-light"
|
||||||
|
label="Default light"
|
||||||
|
style={buttonStyles}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<View style={[defaultPal.view]}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginBottom: 5,
|
||||||
|
}}>
|
||||||
|
<DropdownButton
|
||||||
|
type="primary"
|
||||||
|
items={DROPDOWN_ITEMS}
|
||||||
|
menuWidth={200}
|
||||||
|
label="Primary button"
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
marginBottom: 5,
|
||||||
|
}}>
|
||||||
|
<DropdownButton type="bare" items={DROPDOWN_ITEMS} menuWidth={200}>
|
||||||
|
<Text>Bare</Text>
|
||||||
|
</DropdownButton>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ToggleButtonsView() {
|
||||||
|
const defaultPal = usePalette('default')
|
||||||
|
const buttonStyles = {marginBottom: 5}
|
||||||
|
const [isSelected, setIsSelected] = React.useState(false)
|
||||||
|
const onToggle = () => setIsSelected(!isSelected)
|
||||||
|
return (
|
||||||
|
<View style={[defaultPal.view]}>
|
||||||
|
<ToggleButton
|
||||||
|
type="primary"
|
||||||
|
label="Primary solid"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="secondary"
|
||||||
|
label="Secondary solid"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="inverted"
|
||||||
|
label="Inverted solid"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="primary-outline"
|
||||||
|
label="Primary outline"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="secondary-outline"
|
||||||
|
label="Secondary outline"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="primary-light"
|
||||||
|
label="Primary light"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="secondary-light"
|
||||||
|
label="Secondary light"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
<ToggleButton
|
||||||
|
type="default-light"
|
||||||
|
label="Default light"
|
||||||
|
style={buttonStyles}
|
||||||
|
isSelected={isSelected}
|
||||||
|
onPress={onToggle}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<View style={[defaultPal.view]}>
|
||||||
|
<RadioGroup
|
||||||
|
type={rgType}
|
||||||
|
items={RADIO_BUTTON_ITEMS}
|
||||||
|
initialSelection="default-light"
|
||||||
|
onSelect={setRgType}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
|
@ -6,11 +6,11 @@ import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Feed} from '../com/posts/Feed'
|
import {Feed} from '../com/posts/Feed'
|
||||||
import {Text} from '../com/util/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {s, colors} from '../lib/styles'
|
import {s, colors} from '../lib/styles'
|
||||||
import {useOnMainScroll} from '../lib/useOnMainScroll'
|
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
|
||||||
import {clamp} from 'lodash'
|
import {clamp} from 'lodash'
|
||||||
|
|
||||||
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
|
const HITSLOP = {left: 20, top: 20, right: 20, bottom: 20}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {observer} from 'mobx-react-lite'
|
||||||
import {Signin} from '../com/login/Signin'
|
import {Signin} from '../com/login/Signin'
|
||||||
import {Logo} from '../com/login/Logo'
|
import {Logo} from '../com/login/Logo'
|
||||||
import {CreateAccount} from '../com/login/CreateAccount'
|
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'
|
import {s, colors} from '../lib/styles'
|
||||||
|
|
||||||
enum ScreenState {
|
enum ScreenState {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {Button, View} from 'react-native'
|
import {Button, View} from 'react-native'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Text} from '../com/util/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
|
|
||||||
export const NotFound = () => {
|
export const NotFound = () => {
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Feed} from '../com/notifications/Feed'
|
import {Feed} from '../com/notifications/Feed'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useOnMainScroll} from '../lib/useOnMainScroll'
|
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
|
||||||
|
|
||||||
export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
export const Notifications = ({navIdx, visible}: ScreenParams) => {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
|
|
|
@ -12,14 +12,14 @@ import {ProfileHeader} from '../com/profile/ProfileHeader'
|
||||||
import {FeedItem} from '../com/posts/FeedItem'
|
import {FeedItem} from '../com/posts/FeedItem'
|
||||||
import {ProfileCard} from '../com/profile/ProfileCard'
|
import {ProfileCard} from '../com/profile/ProfileCard'
|
||||||
import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '../com/util/LoadingPlaceholder'
|
||||||
import {ErrorScreen} from '../com/util/ErrorScreen'
|
import {ErrorScreen} from '../com/util/error/ErrorScreen'
|
||||||
import {ErrorMessage} from '../com/util/ErrorMessage'
|
import {ErrorMessage} from '../com/util/error/ErrorMessage'
|
||||||
import {EmptyState} from '../com/util/EmptyState'
|
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 {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import * as Toast from '../com/util/Toast'
|
import * as Toast from '../com/util/Toast'
|
||||||
import {s, colors} from '../lib/styles'
|
import {s, colors} from '../lib/styles'
|
||||||
import {useOnMainScroll} from '../lib/useOnMainScroll'
|
import {useOnMainScroll} from '../lib/hooks/useOnMainScroll'
|
||||||
|
|
||||||
const LOADING_ITEM = {_reactKey: '__loading__'}
|
const LOADING_ITEM = {_reactKey: '__loading__'}
|
||||||
const END_ITEM = {_reactKey: '__end__'}
|
const END_ITEM = {_reactKey: '__end__'}
|
||||||
|
@ -116,7 +116,6 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
|
||||||
renderItem = (item: any) => (
|
renderItem = (item: any) => (
|
||||||
<View style={s.p5}>
|
<View style={s.p5}>
|
||||||
<ErrorMessage
|
<ErrorMessage
|
||||||
dark
|
|
||||||
message={item.error}
|
message={item.error}
|
||||||
onPressTryAgain={onPressTryAgain}
|
onPressTryAgain={onPressTryAgain}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {SuggestedFollows} from '../com/discover/SuggestedFollows'
|
import {SuggestedFollows} from '../com/discover/SuggestedFollows'
|
||||||
import {UserAvatar} from '../com/util/UserAvatar'
|
import {UserAvatar} from '../com/util/UserAvatar'
|
||||||
import {Text} from '../com/util/Text'
|
import {Text} from '../com/util/text/Text'
|
||||||
import {ScreenParams} from '../routes'
|
import {ScreenParams} from '../routes'
|
||||||
import {useStores} from '../../state'
|
import {useStores} from '../../state'
|
||||||
import {UserAutocompleteViewModel} from '../../state/models/user-autocomplete-view'
|
import {UserAutocompleteViewModel} from '../../state/models/user-autocomplete-view'
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {ScreenParams} from '../routes'
|
||||||
import {s, colors} from '../lib/styles'
|
import {s, colors} from '../lib/styles'
|
||||||
import {ViewHeader} from '../com/util/ViewHeader'
|
import {ViewHeader} from '../com/util/ViewHeader'
|
||||||
import {Link} from '../com/util/Link'
|
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'
|
import {UserAvatar} from '../com/util/UserAvatar'
|
||||||
|
|
||||||
export const Settings = observer(function Settings({
|
export const Settings = observer(function Settings({
|
||||||
|
@ -57,6 +57,9 @@ export const Settings = observer(function Settings({
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Link>
|
</Link>
|
||||||
|
<Link href="/debug" title="Debug tools">
|
||||||
|
<Text style={s.blue3}>Debug tools</Text>
|
||||||
|
</Link>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {observer} from 'mobx-react-lite'
|
||||||
import {Animated, Easing, Platform, StyleSheet, View} from 'react-native'
|
import {Animated, Easing, Platform, StyleSheet, View} from 'react-native'
|
||||||
import {ComposePost} from '../../com/composer/ComposePost'
|
import {ComposePost} from '../../com/composer/ComposePost'
|
||||||
import {ComposerOpts} from '../../../state/models/shell-ui'
|
import {ComposerOpts} from '../../../state/models/shell-ui'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
export const Composer = observer(
|
export const Composer = observer(
|
||||||
({
|
({
|
||||||
|
|
|
@ -17,7 +17,7 @@ import {
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
} from '../../lib/icons'
|
} from '../../lib/icons'
|
||||||
import {UserAvatar} from '../../com/util/UserAvatar'
|
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'
|
import {CreateSceneModal} from '../../../state/models/shell-ui'
|
||||||
|
|
||||||
export const Menu = ({
|
export const Menu = ({
|
||||||
|
|
|
@ -10,13 +10,13 @@ import {
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
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 Swipeable from 'react-native-gesture-handler/Swipeable'
|
||||||
import {useStores} from '../../../state'
|
import {useStores} from '../../../state'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
import {toShareUrl} from '../../../lib/strings'
|
import {toShareUrl} from '../../../lib/strings'
|
||||||
import {match} from '../../routes'
|
import {match} from '../../routes'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
const TAB_HEIGHT = 42
|
const TAB_HEIGHT = 42
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import {Onboard} from '../../screens/Onboard'
|
||||||
import {HorzSwipe} from '../../com/util/gestures/HorzSwipe'
|
import {HorzSwipe} from '../../com/util/gestures/HorzSwipe'
|
||||||
import {Modal} from '../../com/modals/Modal'
|
import {Modal} from '../../com/modals/Modal'
|
||||||
import {Lightbox} from '../../com/lightbox/Lightbox'
|
import {Lightbox} from '../../com/lightbox/Lightbox'
|
||||||
import {Text} from '../../com/util/Text'
|
import {Text} from '../../com/util/text/Text'
|
||||||
import {TabsSelector} from './TabsSelector'
|
import {TabsSelector} from './TabsSelector'
|
||||||
import {Composer} from './Composer'
|
import {Composer} from './Composer'
|
||||||
import {s, colors} from '../../lib/styles'
|
import {s, colors} from '../../lib/styles'
|
||||||
|
@ -42,7 +42,7 @@ import {
|
||||||
BellIcon,
|
BellIcon,
|
||||||
BellIconSolid,
|
BellIconSolid,
|
||||||
} from '../../lib/icons'
|
} from '../../lib/icons'
|
||||||
import {useAnimatedValue} from '../../lib/useAnimatedValue'
|
import {useAnimatedValue} from '../../lib/hooks/useAnimatedValue'
|
||||||
|
|
||||||
const Btn = ({
|
const Btn = ({
|
||||||
icon,
|
icon,
|
||||||
|
|
Loading…
Reference in New Issue