Lex refactor (#362)
* Remove the hackcheck for upgrades * Rename the PostEmbeds folder to match the codebase style * Updates to latest lex refactor * Update to use new bsky agent * Update to use api package's richtext library * Switch to upsertProfile * Add TextEncoder/TextDecoder polyfill * Add Intl.Segmenter polyfill * Update composer to calculate lengths by grapheme * Fix detox * Fix login in e2e * Create account e2e passing * Implement an e2e mocking framework * Don't use private methods on mobx models as mobx can't track them * Add tooling for e2e-specific builds and add e2e media-picker mock * Add some tests and fix some bugs around profile editing * Add shell tests * Add home screen tests * Add thread screen tests * Add tests for other user profile screens * Add search screen tests * Implement profile imagery change tools and tests * Update to new embed behaviors * Add post tests * Fix to profile-screen test * Fix session resumption * Update web composer to new api * 1.11.0 * Fix pagination cursor parameters * Add quote posts to notifications * Fix embed layouts * Remove youtube inline player and improve tap handling on link cards * Reset minimal shell mode on all screen loads and feed swipes (close #299) * Update podfile.lock * Improve post notfound UI (close #366) * Bump atproto packages
This commit is contained in:
parent
19f3a2fa92
commit
a3334a01a2
133 changed files with 3103 additions and 2839 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React, {useEffect, useRef, useState} from 'react'
|
||||
import React from 'react'
|
||||
import {observer} from 'mobx-react-lite'
|
||||
import {
|
||||
ActivityIndicator,
|
||||
|
|
@ -13,6 +13,7 @@ import {
|
|||
} from 'react-native'
|
||||
import LinearGradient from 'react-native-linear-gradient'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {RichText} from '@atproto/api'
|
||||
import {useAnalytics} from 'lib/analytics'
|
||||
import {UserAutocompleteViewModel} from 'state/models/user-autocomplete-view'
|
||||
import {ExternalEmbed} from './ExternalEmbed'
|
||||
|
|
@ -30,11 +31,11 @@ import {SelectPhotoBtn} from './photos/SelectPhotoBtn'
|
|||
import {OpenCameraBtn} from './photos/OpenCameraBtn'
|
||||
import {SelectedPhotos} from './photos/SelectedPhotos'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import QuoteEmbed from '../util/PostEmbeds/QuoteEmbed'
|
||||
import QuoteEmbed from '../util/post-embeds/QuoteEmbed'
|
||||
import {useExternalLinkFetch} from './useExternalLinkFetch'
|
||||
import {isDesktopWeb} from 'platform/detection'
|
||||
|
||||
const MAX_TEXT_LENGTH = 256
|
||||
const MAX_GRAPHEME_LENGTH = 300
|
||||
|
||||
export const ComposePost = observer(function ComposePost({
|
||||
replyTo,
|
||||
|
|
@ -50,17 +51,23 @@ export const ComposePost = observer(function ComposePost({
|
|||
const {track} = useAnalytics()
|
||||
const pal = usePalette('default')
|
||||
const store = useStores()
|
||||
const textInput = useRef<TextInputRef>(null)
|
||||
const [isProcessing, setIsProcessing] = useState(false)
|
||||
const [processingState, setProcessingState] = useState('')
|
||||
const [error, setError] = useState('')
|
||||
const [text, setText] = useState('')
|
||||
const [quote, setQuote] = useState<ComposerOpts['quote'] | undefined>(
|
||||
const textInput = React.useRef<TextInputRef>(null)
|
||||
const [isProcessing, setIsProcessing] = React.useState(false)
|
||||
const [processingState, setProcessingState] = React.useState('')
|
||||
const [error, setError] = React.useState('')
|
||||
const [richtext, setRichText] = React.useState(new RichText({text: ''}))
|
||||
const graphemeLength = React.useMemo(
|
||||
() => richtext.graphemeLength,
|
||||
[richtext],
|
||||
)
|
||||
const [quote, setQuote] = React.useState<ComposerOpts['quote'] | undefined>(
|
||||
initQuote,
|
||||
)
|
||||
const {extLink, setExtLink} = useExternalLinkFetch({setQuote})
|
||||
const [suggestedLinks, setSuggestedLinks] = useState<Set<string>>(new Set())
|
||||
const [selectedPhotos, setSelectedPhotos] = useState<string[]>([])
|
||||
const [suggestedLinks, setSuggestedLinks] = React.useState<Set<string>>(
|
||||
new Set(),
|
||||
)
|
||||
const [selectedPhotos, setSelectedPhotos] = React.useState<string[]>([])
|
||||
|
||||
const autocompleteView = React.useMemo<UserAutocompleteViewModel>(
|
||||
() => new UserAutocompleteViewModel(store),
|
||||
|
|
@ -78,11 +85,11 @@ export const ComposePost = observer(function ComposePost({
|
|||
}, [textInput, onClose])
|
||||
|
||||
// initial setup
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
autocompleteView.setup()
|
||||
}, [autocompleteView])
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
// HACK
|
||||
// wait a moment before focusing the input to resolve some layout bugs with the keyboard-avoiding-view
|
||||
// -prf
|
||||
|
|
@ -132,18 +139,18 @@ export const ComposePost = observer(function ComposePost({
|
|||
if (isProcessing) {
|
||||
return
|
||||
}
|
||||
if (text.length > MAX_TEXT_LENGTH) {
|
||||
if (richtext.graphemeLength > MAX_GRAPHEME_LENGTH) {
|
||||
return
|
||||
}
|
||||
setError('')
|
||||
if (text.trim().length === 0 && selectedPhotos.length === 0) {
|
||||
if (richtext.text.trim().length === 0 && selectedPhotos.length === 0) {
|
||||
setError('Did you want to say anything?')
|
||||
return false
|
||||
}
|
||||
setIsProcessing(true)
|
||||
try {
|
||||
await apilib.post(store, {
|
||||
rawText: text,
|
||||
rawText: richtext.text,
|
||||
replyTo: replyTo?.uri,
|
||||
images: selectedPhotos,
|
||||
quote: quote,
|
||||
|
|
@ -172,7 +179,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
Toast.show(`Your ${replyTo ? 'reply' : 'post'} has been published`)
|
||||
}, [
|
||||
isProcessing,
|
||||
text,
|
||||
richtext,
|
||||
setError,
|
||||
setIsProcessing,
|
||||
replyTo,
|
||||
|
|
@ -187,7 +194,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
track,
|
||||
])
|
||||
|
||||
const canPost = text.length <= MAX_TEXT_LENGTH
|
||||
const canPost = graphemeLength <= MAX_GRAPHEME_LENGTH
|
||||
|
||||
const selectTextInputPlaceholder = replyTo
|
||||
? 'Write your reply'
|
||||
|
|
@ -215,7 +222,7 @@ export const ComposePost = observer(function ComposePost({
|
|||
</View>
|
||||
) : canPost ? (
|
||||
<TouchableOpacity
|
||||
testID="composerPublishButton"
|
||||
testID="composerPublishBtn"
|
||||
onPress={onPressPublish}>
|
||||
<LinearGradient
|
||||
colors={[gradients.blueLight.start, gradients.blueLight.end]}
|
||||
|
|
@ -271,42 +278,41 @@ export const ComposePost = observer(function ComposePost({
|
|||
<UserAvatar avatar={store.me.avatar} size={50} />
|
||||
<TextInput
|
||||
ref={textInput}
|
||||
text={text}
|
||||
richtext={richtext}
|
||||
placeholder={selectTextInputPlaceholder}
|
||||
suggestedLinks={suggestedLinks}
|
||||
autocompleteView={autocompleteView}
|
||||
onTextChanged={setText}
|
||||
setRichText={setRichText}
|
||||
onPhotoPasted={onPhotoPasted}
|
||||
onSuggestedLinksChanged={setSuggestedLinks}
|
||||
onError={setError}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{quote ? (
|
||||
<View style={s.mt5}>
|
||||
<QuoteEmbed quote={quote} />
|
||||
</View>
|
||||
) : undefined}
|
||||
|
||||
<SelectedPhotos
|
||||
selectedPhotos={selectedPhotos}
|
||||
onSelectPhotos={onSelectPhotos}
|
||||
/>
|
||||
{!selectedPhotos.length && extLink && (
|
||||
{selectedPhotos.length === 0 && extLink && (
|
||||
<ExternalEmbed
|
||||
link={extLink}
|
||||
onRemove={() => setExtLink(undefined)}
|
||||
/>
|
||||
)}
|
||||
{quote ? (
|
||||
<View style={s.mt5}>
|
||||
<QuoteEmbed quote={quote} />
|
||||
</View>
|
||||
) : undefined}
|
||||
</ScrollView>
|
||||
{!extLink &&
|
||||
selectedPhotos.length === 0 &&
|
||||
suggestedLinks.size > 0 &&
|
||||
!quote ? (
|
||||
suggestedLinks.size > 0 ? (
|
||||
<View style={s.mb5}>
|
||||
{Array.from(suggestedLinks).map(url => (
|
||||
<TouchableOpacity
|
||||
key={`suggested-${url}`}
|
||||
testID="addLinkCardBtn"
|
||||
style={[pal.borderDark, styles.addExtLinkBtn]}
|
||||
onPress={() => onPressAddLinkCard(url)}>
|
||||
<Text style={pal.text}>
|
||||
|
|
@ -318,17 +324,17 @@ export const ComposePost = observer(function ComposePost({
|
|||
) : null}
|
||||
<View style={[pal.border, styles.bottomBar]}>
|
||||
<SelectPhotoBtn
|
||||
enabled={!quote && selectedPhotos.length < 4}
|
||||
enabled={selectedPhotos.length < 4}
|
||||
selectedPhotos={selectedPhotos}
|
||||
onSelectPhotos={setSelectedPhotos}
|
||||
/>
|
||||
<OpenCameraBtn
|
||||
enabled={!quote && selectedPhotos.length < 4}
|
||||
enabled={selectedPhotos.length < 4}
|
||||
selectedPhotos={selectedPhotos}
|
||||
onSelectPhotos={setSelectedPhotos}
|
||||
/>
|
||||
<View style={s.flex1} />
|
||||
<CharProgress count={text.length} />
|
||||
<CharProgress count={graphemeLength} />
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</TouchableWithoutFeedback>
|
||||
|
|
@ -408,6 +414,7 @@ const styles = StyleSheet.create({
|
|||
borderRadius: 24,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 12,
|
||||
marginHorizontal: 10,
|
||||
marginBottom: 4,
|
||||
},
|
||||
bottomBar: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue