merge in main
This commit is contained in:
@@ -15,8 +15,24 @@ export function SendViaChatDialog({
|
||||
control: Dialog.DialogControlProps
|
||||
onSelectChat: (chatId: string) => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<Dialog.Outer
|
||||
control={control}
|
||||
testID="sendViaChatChatDialog"
|
||||
nativeOptions={{sheet: {snapPoints: ['100%']}}}>
|
||||
<SendViaChatDialogInner control={control} onSelectChat={onSelectChat} />
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
function SendViaChatDialogInner({
|
||||
control,
|
||||
onSelectChat,
|
||||
}: {
|
||||
control: Dialog.DialogControlProps
|
||||
onSelectChat: (chatId: string) => void
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const {mutate: createChat} = useGetConvoForMembers({
|
||||
onSuccess: data => {
|
||||
onSelectChat(data.convo.id)
|
||||
@@ -39,15 +55,10 @@ export function SendViaChatDialog({
|
||||
)
|
||||
|
||||
return (
|
||||
<Dialog.Outer
|
||||
control={control}
|
||||
testID="sendViaChatChatDialog"
|
||||
nativeOptions={{sheet: {snapPoints: ['100%']}}}>
|
||||
<SearchablePeopleList
|
||||
title={_(msg`Send post to...`)}
|
||||
onSelectChat={onCreateChat}
|
||||
showRecentConvos
|
||||
/>
|
||||
</Dialog.Outer>
|
||||
<SearchablePeopleList
|
||||
title={_(msg`Send post to...`)}
|
||||
onSelectChat={onCreateChat}
|
||||
showRecentConvos
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ import {
|
||||
launchImageLibraryAsync,
|
||||
MediaTypeOptions,
|
||||
} from 'expo-image-picker'
|
||||
import {getDataUriSize} from './util'
|
||||
|
||||
import * as Toast from 'view/com/util/Toast'
|
||||
import {getDataUriSize} from './util'
|
||||
|
||||
export async function openPicker(opts?: ImagePickerOptions) {
|
||||
const response = await launchImageLibraryAsync({
|
||||
@@ -12,6 +13,7 @@ export async function openPicker(opts?: ImagePickerOptions) {
|
||||
mediaTypes: MediaTypeOptions.Images,
|
||||
quality: 1,
|
||||
...opts,
|
||||
legacy: true,
|
||||
})
|
||||
|
||||
if (response.assets && response.assets.length > 4) {
|
||||
|
||||
@@ -3,4 +3,5 @@ export type Gate =
|
||||
| 'request_notifications_permission_after_onboarding_v2'
|
||||
| 'show_avi_follow_button'
|
||||
| 'show_follow_back_label_v2'
|
||||
| 'show_notification_badge_mobile_web'
|
||||
| 'starter_packs_enabled'
|
||||
|
||||
@@ -102,6 +102,7 @@ export function StepProfile() {
|
||||
mediaTypes: MediaTypeOptions.Images,
|
||||
quality: 1,
|
||||
...opts,
|
||||
legacy: true,
|
||||
})
|
||||
|
||||
return (response.assets ?? [])
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from 'react'
|
||||
import {
|
||||
Easing,
|
||||
cancelAnimation,
|
||||
SharedValue,
|
||||
useSharedValue,
|
||||
withTiming,
|
||||
withSpring,
|
||||
} from 'react-native-reanimated'
|
||||
|
||||
type StateContext = SharedValue<number>
|
||||
@@ -22,9 +22,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const setMode = React.useCallback(
|
||||
(v: boolean) => {
|
||||
'worklet'
|
||||
mode.value = withTiming(v ? 1 : 0, {
|
||||
duration: 400,
|
||||
easing: Easing.bezier(0.25, 0.1, 0.25, 1),
|
||||
// Cancel any existing animation
|
||||
cancelAnimation(mode)
|
||||
mode.value = withSpring(v ? 1 : 0, {
|
||||
overshootClamping: true,
|
||||
})
|
||||
},
|
||||
[mode],
|
||||
|
||||
@@ -107,7 +107,9 @@ export const ComposePost = observer(function ComposePost({
|
||||
text: initText,
|
||||
imageUris: initImageUris,
|
||||
cancelRef,
|
||||
isModalReady,
|
||||
}: Props & {
|
||||
isModalReady: boolean
|
||||
cancelRef?: React.RefObject<CancelRef>
|
||||
}) {
|
||||
const {currentAccount} = useSession()
|
||||
@@ -155,12 +157,6 @@ export const ComposePost = observer(function ComposePost({
|
||||
const [labels, setLabels] = useState<string[]>([])
|
||||
const [threadgate, setThreadgate] = useState<ThreadgateSetting[]>([])
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isAndroid) return
|
||||
const id = setTimeout(() => textInput.current?.focus(), 100)
|
||||
return () => clearTimeout(id)
|
||||
}, [])
|
||||
|
||||
const gallery = useMemo(
|
||||
() => new GalleryModel(initImageUris),
|
||||
[initImageUris],
|
||||
@@ -181,9 +177,7 @@ export const ComposePost = observer(function ComposePost({
|
||||
const onPressCancel = useCallback(() => {
|
||||
if (graphemeLength > 0 || !gallery.isEmpty || extGif) {
|
||||
closeAllDialogs()
|
||||
if (Keyboard) {
|
||||
Keyboard.dismiss()
|
||||
}
|
||||
Keyboard.dismiss()
|
||||
discardPromptControl.open()
|
||||
} else {
|
||||
onClose()
|
||||
@@ -524,7 +518,11 @@ export const ComposePost = observer(function ComposePost({
|
||||
ref={textInput}
|
||||
richtext={richtext}
|
||||
placeholder={selectTextInputPlaceholder}
|
||||
autoFocus={!isAndroid}
|
||||
// fixes autofocus on android
|
||||
key={
|
||||
isAndroid ? (isModalReady ? 'ready' : 'animating') : 'static'
|
||||
}
|
||||
autoFocus={isAndroid ? isModalReady : true}
|
||||
setRichText={setRichText}
|
||||
onPhotoPasted={onPhotoPasted}
|
||||
onPressPublish={onPressPublish}
|
||||
|
||||
@@ -1,30 +1,36 @@
|
||||
import React, {useState} from 'react'
|
||||
|
||||
import {ActivityIndicator, Dimensions, StyleSheet} from 'react-native'
|
||||
import {Image} from 'expo-image'
|
||||
import {Gesture, GestureDetector} from 'react-native-gesture-handler'
|
||||
import Animated, {
|
||||
runOnJS,
|
||||
useAnimatedReaction,
|
||||
useAnimatedRef,
|
||||
useAnimatedStyle,
|
||||
useAnimatedReaction,
|
||||
useSharedValue,
|
||||
withDecay,
|
||||
withSpring,
|
||||
} from 'react-native-reanimated'
|
||||
import {GestureDetector, Gesture} from 'react-native-gesture-handler'
|
||||
import {Image} from 'expo-image'
|
||||
|
||||
import type {Dimensions as ImageDimensions, ImageSource} from '../../@types'
|
||||
import useImageDimensions from '../../hooks/useImageDimensions'
|
||||
import {
|
||||
createTransform,
|
||||
readTransform,
|
||||
applyRounding,
|
||||
createTransform,
|
||||
prependPan,
|
||||
prependPinch,
|
||||
prependTransform,
|
||||
readTransform,
|
||||
TransformMatrix,
|
||||
} from '../../transforms'
|
||||
import type {ImageSource, Dimensions as ImageDimensions} from '../../@types'
|
||||
|
||||
const SCREEN = Dimensions.get('window')
|
||||
const windowDim = Dimensions.get('window')
|
||||
const screenDim = Dimensions.get('screen')
|
||||
const statusBarHeight = windowDim.height - screenDim.height
|
||||
const SCREEN = {
|
||||
width: windowDim.width,
|
||||
height: windowDim.height + statusBarHeight,
|
||||
}
|
||||
const MIN_DOUBLE_TAP_SCALE = 2
|
||||
const MAX_ORIGINAL_IMAGE_ZOOM = 2
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import React, {useCallback, useEffect} from 'react'
|
||||
import {NativeScrollEvent} from 'react-native'
|
||||
import {interpolate, useSharedValue} from 'react-native-reanimated'
|
||||
import {
|
||||
cancelAnimation,
|
||||
interpolate,
|
||||
useSharedValue,
|
||||
} from 'react-native-reanimated'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
|
||||
import {ScrollProvider} from '#/lib/ScrollContext'
|
||||
@@ -117,6 +121,8 @@ export function MainScrollProvider({children}: {children: React.ReactNode}) {
|
||||
const newValue = clamp(startMode.value + dProgress, 0, 1)
|
||||
if (newValue !== mode.value) {
|
||||
// Manually adjust the value. This won't be (and shouldn't be) animated.
|
||||
// Cancel any any existing animation
|
||||
cancelAnimation(mode)
|
||||
mode.value = newValue
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, {useLayoutEffect} from 'react'
|
||||
import React, {useLayoutEffect, useState} from 'react'
|
||||
import {Modal, View} from 'react-native'
|
||||
import {GestureHandlerRootView} from 'react-native-gesture-handler'
|
||||
import {RootSiblingParent} from 'react-native-root-siblings'
|
||||
@@ -24,8 +24,16 @@ export const Composer = observer(function ComposerImpl({}: {
|
||||
const t = useTheme()
|
||||
const state = useComposerState()
|
||||
const ref = useComposerCancelRef()
|
||||
const [isModalReady, setIsModalReady] = useState(false)
|
||||
|
||||
const open = !!state
|
||||
const [prevOpen, setPrevOpen] = useState(open)
|
||||
if (open !== prevOpen) {
|
||||
setPrevOpen(open)
|
||||
if (!open) {
|
||||
setIsModalReady(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -34,10 +42,12 @@ export const Composer = observer(function ComposerImpl({}: {
|
||||
visible={open}
|
||||
presentationStyle="formSheet"
|
||||
animationType="slide"
|
||||
onShow={() => setIsModalReady(true)}
|
||||
onRequestClose={() => ref.current?.onPressCancel()}>
|
||||
<View style={[t.atoms.bg, a.flex_1]}>
|
||||
<Providers open={open}>
|
||||
<ComposePost
|
||||
isModalReady={isModalReady}
|
||||
cancelRef={ref}
|
||||
replyTo={state?.replyTo}
|
||||
onPost={state?.onPost}
|
||||
|
||||
@@ -56,6 +56,7 @@ export function Composer({}: {winHeight: number}) {
|
||||
t.atoms.border_contrast_medium,
|
||||
]}>
|
||||
<ComposePost
|
||||
isModalReady={true}
|
||||
replyTo={state.replyTo}
|
||||
quote={state.quote}
|
||||
onPost={state.onPost}
|
||||
|
||||
@@ -16,6 +16,9 @@ import {s} from '#/lib/styles'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useLoggedOutViewControls} from '#/state/shell/logged-out'
|
||||
import {useCloseAllActiveElements} from '#/state/util'
|
||||
import {useGate} from 'lib/statsig/statsig'
|
||||
import {useUnreadMessageCount} from 'state/queries/messages/list-converations'
|
||||
import {useUnreadNotifications} from 'state/queries/notifications/unread'
|
||||
import {Button} from '#/view/com/util/forms/Button'
|
||||
import {Text} from '#/view/com/util/text/Text'
|
||||
import {Logo} from '#/view/icons/Logo'
|
||||
@@ -46,11 +49,15 @@ export function BottomBarWeb() {
|
||||
const {hasSession, currentAccount} = useSession()
|
||||
const pal = usePalette('default')
|
||||
const safeAreaInsets = useSafeAreaInsets()
|
||||
const gate = useGate()
|
||||
const {footerMinimalShellTransform} = useMinimalShellMode()
|
||||
const {requestSwitchToAccount} = useLoggedOutViewControls()
|
||||
const closeAllActiveElements = useCloseAllActiveElements()
|
||||
const iconWidth = 26
|
||||
|
||||
const unreadMessageCount = useUnreadMessageCount()
|
||||
const notificationCountStr = useUnreadNotifications()
|
||||
|
||||
const showSignIn = React.useCallback(() => {
|
||||
closeAllActiveElements()
|
||||
requestSwitchToAccount({requestedAccount: 'none'})
|
||||
@@ -103,10 +110,20 @@ export function BottomBarWeb() {
|
||||
{({isActive}) => {
|
||||
const Icon = isActive ? MessageFilled : Message
|
||||
return (
|
||||
<Icon
|
||||
width={iconWidth - 1}
|
||||
style={[styles.ctrlIcon, pal.text, styles.messagesIcon]}
|
||||
/>
|
||||
<>
|
||||
<Icon
|
||||
width={iconWidth - 1}
|
||||
style={[styles.ctrlIcon, pal.text, styles.messagesIcon]}
|
||||
/>
|
||||
{unreadMessageCount.count > 0 &&
|
||||
gate('show_notification_badge_mobile_web') && (
|
||||
<View style={styles.notificationCount}>
|
||||
<Text style={styles.notificationCountLabel}>
|
||||
{unreadMessageCount.numUnread}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</NavItem>
|
||||
@@ -114,10 +131,20 @@ export function BottomBarWeb() {
|
||||
{({isActive}) => {
|
||||
const Icon = isActive ? BellFilled : Bell
|
||||
return (
|
||||
<Icon
|
||||
width={iconWidth}
|
||||
style={[styles.ctrlIcon, pal.text, styles.bellIcon]}
|
||||
/>
|
||||
<>
|
||||
<Icon
|
||||
width={iconWidth}
|
||||
style={[styles.ctrlIcon, pal.text, styles.bellIcon]}
|
||||
/>
|
||||
{notificationCountStr !== '' &&
|
||||
gate('show_notification_badge_mobile_web') && (
|
||||
<View style={styles.notificationCount}>
|
||||
<Text style={styles.notificationCountLabel}>
|
||||
{notificationCountStr}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</NavItem>
|
||||
|
||||
Reference in New Issue
Block a user