diff --git a/src/App.native.tsx b/src/App.native.tsx index 796666f316..30a5e81296 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -3,7 +3,6 @@ import '#/logger/bitdrift/setup' import '#/view/icons' import React, {useEffect, useState} from 'react' -import {View} from 'react-native' import {GestureHandlerRootView} from 'react-native-gesture-handler' import { initialWindowMetrics, @@ -61,26 +60,22 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide' import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed' import {Provider as StarterPackProvider} from '#/state/shell/starter-pack' import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies' +import {TestCtrls} from '#/view/com/testing/TestCtrls' import * as Toast from '#/view/com/util/Toast' -import {atoms as a, ThemeProvider as Alf} from '#/alf' +import {Shell} from '#/view/shell' +import {ThemeProvider as Alf} from '#/alf' import {useColorModeTheme} from '#/alf/util/useColorModeTheme' -import {Button, ButtonText} from '#/components/Button' import {Provider as ContextMenuProvider} from '#/components/ContextMenu' -import * as Dialog from '#/components/Dialog' +import {NuxDialogs} from '#/components/dialogs/nuxs' import {useStarterPackEntry} from '#/components/hooks/useStarterPackEntry' import {Provider as IntentDialogProvider} from '#/components/intents/IntentDialogs' import {Provider as PolicyUpdateOverlayProvider} from '#/components/PolicyUpdateOverlay' -import {Outlet as PortalOutlet} from '#/components/Portal' import {Provider as PortalProvider} from '#/components/Portal' import {Provider as VideoVolumeProvider} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' import {ToastOutlet} from '#/components/Toast' import {Splash} from '#/Splash' -import {BottomSheetOutlet} from '../modules/bottom-sheet' import {BottomSheetProvider} from '../modules/bottom-sheet' import {BackgroundNotificationPreferencesProvider} from '../modules/expo-background-notification-handler/src/BackgroundNotificationHandlerProvider' -import {useApplyPullRequestOTAUpdate} from './lib/hooks/useOTAUpdates' -import {RequestListItem} from './screens/Messages/components/RequestListItem' -import {List} from './view/com/util/List' SplashScreen.preventAutoHideAsync() if (isIOS) { @@ -169,10 +164,10 @@ function InnerApp() { style={s.h100pct}> - + + + - - @@ -257,104 +252,3 @@ function App() { } export default Sentry.wrap(App) - -function Testbed() { - const control = Dialog.useDialogControl() - const [showItem, setShowItem] = useState(true) - - const {revertToEmbedded} = useApplyPullRequestOTAUpdate() - - return ( - - - - ( - - )} - /> - - - - - - - - - - - - ) -} diff --git a/src/screens/Messages/components/ChatListItem.tsx b/src/screens/Messages/components/ChatListItem.tsx index 1b260c1c42..f4e7863679 100644 --- a/src/screens/Messages/components/ChatListItem.tsx +++ b/src/screens/Messages/components/ChatListItem.tsx @@ -30,14 +30,16 @@ import { import {precacheProfile} from '#/state/queries/profile' import {useSession} from '#/state/session' import {TimeElapsed} from '#/view/com/util/TimeElapsed' -import {UserAvatar} from '#/view/com/util/UserAvatar' -import {atoms as a, useTheme, web} from '#/alf' +import {PreviewableUserAvatar} from '#/view/com/util/UserAvatar' +import {atoms as a, useBreakpoints, useTheme, web} from '#/alf' import * as tokens from '#/alf/tokens' -import {Button} from '#/components/Button' import {useDialogControl} from '#/components/Dialog' +import {ConvoMenu} from '#/components/dms/ConvoMenu' +import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt' import {Bell2Off_Filled_Corner0_Rounded as BellStroke} from '#/components/icons/Bell2' import {Envelope_Open_Stroke2_Corner0_Rounded as EnvelopeOpen} from '#/components/icons/EnveopeOpen' import {Trash_Stroke2_Corner0_Rounded} from '#/components/icons/Trash' +import {Link} from '#/components/Link' import {useMenuControl} from '#/components/Menu' import {PostAlerts} from '#/components/moderation/PostAlerts' import {createPortalGroup} from '#/components/Portal' @@ -85,6 +87,7 @@ function ChatListItemReady({ convo, profile: profileUnshadowed, moderationOpts, + showMenu, children, }: { convo: ChatBskyConvoDefs.ConvoView @@ -98,6 +101,7 @@ function ChatListItemReady({ const {currentAccount} = useSession() const menuControl = useMenuControl() const leaveConvoControl = useDialogControl() + const {gtMobile} = useBreakpoints() const profile = useProfileShadow(profileUnshadowed) const {mutate: markAsRead} = useMarkAsReadMutation() const moderation = React.useMemo( @@ -111,6 +115,17 @@ function ChatListItemReady({ profile, }) + const blockInfo = useMemo(() => { + const modui = moderation.ui('profileView') + const blocks = modui.alerts.filter(alert => alert.type === 'blocking') + const listBlocks = blocks.filter(alert => alert.source.type === 'list') + const userBlock = blocks.find(alert => alert.source.type === 'user') + return { + listBlocks, + userBlock, + } + }, [moderation]) + const isDeletedAccount = profile.handle === 'missing.invalid' const displayName = isDeletedAccount ? _(msg`Deleted Account`) @@ -121,134 +136,137 @@ function ChatListItemReady({ const isDimStyle = convo.muted || moderation.blocked || isDeletedAccount - const {lastMessage, lastMessageSentAt} = useMemo(() => { - // eslint-disable-next-line @typescript-eslint/no-shadow - let lastMessage = _(msg`No messages yet`) - // eslint-disable-next-line @typescript-eslint/no-shadow - let lastMessageSentAt: string | null = null + const {lastMessage, lastMessageSentAt, latestReportableMessage} = + useMemo(() => { + // eslint-disable-next-line @typescript-eslint/no-shadow + let lastMessage = _(msg`No messages yet`) + // eslint-disable-next-line @typescript-eslint/no-shadow + let lastMessageSentAt: string | null = null + // eslint-disable-next-line @typescript-eslint/no-shadow + let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined - let latestReportableMessage: ChatBskyConvoDefs.MessageView | undefined + if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) { + const isFromMe = convo.lastMessage.sender?.did === currentAccount?.did - if (ChatBskyConvoDefs.isMessageView(convo.lastMessage)) { - const isFromMe = convo.lastMessage.sender?.did === currentAccount?.did - - if (!isFromMe) { - latestReportableMessage = convo.lastMessage - } - - if (convo.lastMessage.text) { - if (isFromMe) { - lastMessage = _(msg`You: ${convo.lastMessage.text}`) - } else { - lastMessage = convo.lastMessage.text + if (!isFromMe) { + latestReportableMessage = convo.lastMessage } - } else if (convo.lastMessage.embed) { - const defaultEmbeddedContentMessage = _( - msg`(contains embedded content)`, - ) - if (AppBskyEmbedRecord.isView(convo.lastMessage.embed)) { - const embed = convo.lastMessage.embed + if (convo.lastMessage.text) { + if (isFromMe) { + lastMessage = _(msg`You: ${convo.lastMessage.text}`) + } else { + lastMessage = convo.lastMessage.text + } + } else if (convo.lastMessage.embed) { + const defaultEmbeddedContentMessage = _( + msg`(contains embedded content)`, + ) - if (AppBskyEmbedRecord.isViewRecord(embed.record)) { - const record = embed.record - const path = postUriToRelativePath(record.uri, { - handle: record.author.handle, - }) - const href = path ? toBskyAppUrl(path) : undefined - const short = href - ? toShortUrl(href) - : defaultEmbeddedContentMessage + if (AppBskyEmbedRecord.isView(convo.lastMessage.embed)) { + const embed = convo.lastMessage.embed + + if (AppBskyEmbedRecord.isViewRecord(embed.record)) { + const record = embed.record + const path = postUriToRelativePath(record.uri, { + handle: record.author.handle, + }) + const href = path ? toBskyAppUrl(path) : undefined + const short = href + ? toShortUrl(href) + : defaultEmbeddedContentMessage + if (isFromMe) { + lastMessage = _(msg`You: ${short}`) + } else { + lastMessage = short + } + } + } else { if (isFromMe) { - lastMessage = _(msg`You: ${short}`) + lastMessage = _(msg`You: ${defaultEmbeddedContentMessage}`) } else { - lastMessage = short + lastMessage = defaultEmbeddedContentMessage } } - } else { - if (isFromMe) { - lastMessage = _(msg`You: ${defaultEmbeddedContentMessage}`) - } else { - lastMessage = defaultEmbeddedContentMessage - } } + + lastMessageSentAt = convo.lastMessage.sentAt + } + if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) { + lastMessageSentAt = convo.lastMessage.sentAt + + lastMessage = isDeletedAccount + ? _(msg`Conversation deleted`) + : _(msg`Message deleted`) } - lastMessageSentAt = convo.lastMessage.sentAt - } - if (ChatBskyConvoDefs.isDeletedMessageView(convo.lastMessage)) { - lastMessageSentAt = convo.lastMessage.sentAt - - lastMessage = isDeletedAccount - ? _(msg`Conversation deleted`) - : _(msg`Message deleted`) - } - - if (ChatBskyConvoDefs.isMessageAndReactionView(convo.lastReaction)) { - if ( - !lastMessageSentAt || - new Date(lastMessageSentAt) < - new Date(convo.lastReaction.reaction.createdAt) - ) { - const isFromMe = - convo.lastReaction.reaction.sender.did === currentAccount?.did - const lastMessageText = convo.lastReaction.message.text - const fallbackMessage = _( - msg({ - message: 'a message', - comment: `If last message does not contain text, fall back to "{user} reacted to {a message}"`, - }), - ) - - if (isFromMe) { - lastMessage = _( - msg`You reacted ${convo.lastReaction.reaction.value} to ${ - lastMessageText - ? `"${convo.lastReaction.message.text}"` - : fallbackMessage - }`, + if (ChatBskyConvoDefs.isMessageAndReactionView(convo.lastReaction)) { + if ( + !lastMessageSentAt || + new Date(lastMessageSentAt) < + new Date(convo.lastReaction.reaction.createdAt) + ) { + const isFromMe = + convo.lastReaction.reaction.sender.did === currentAccount?.did + const lastMessageText = convo.lastReaction.message.text + const fallbackMessage = _( + msg({ + message: 'a message', + comment: `If last message does not contain text, fall back to "{user} reacted to {a message}"`, + }), ) - } else { - const senderDid = convo.lastReaction.reaction.sender.did - const sender = convo.members.find(member => member.did === senderDid) - if (sender) { + + if (isFromMe) { lastMessage = _( - msg`${sanitizeDisplayName( - sender.displayName || sender.handle, - )} reacted ${convo.lastReaction.reaction.value} to ${ + msg`You reacted ${convo.lastReaction.reaction.value} to ${ lastMessageText ? `"${convo.lastReaction.message.text}"` : fallbackMessage }`, ) } else { - lastMessage = _( - msg`Someone reacted ${convo.lastReaction.reaction.value} to ${ - lastMessageText - ? `"${convo.lastReaction.message.text}"` - : fallbackMessage - }`, + const senderDid = convo.lastReaction.reaction.sender.did + const sender = convo.members.find( + member => member.did === senderDid, ) + if (sender) { + lastMessage = _( + msg`${sanitizeDisplayName( + sender.displayName || sender.handle, + )} reacted ${convo.lastReaction.reaction.value} to ${ + lastMessageText + ? `"${convo.lastReaction.message.text}"` + : fallbackMessage + }`, + ) + } else { + lastMessage = _( + msg`Someone reacted ${convo.lastReaction.reaction.value} to ${ + lastMessageText + ? `"${convo.lastReaction.message.text}"` + : fallbackMessage + }`, + ) + } } } } - } - return { - lastMessage, - lastMessageSentAt, - latestReportableMessage, - } - }, [ - _, - convo.lastMessage, - convo.lastReaction, - currentAccount?.did, - isDeletedAccount, - convo.members, - ]) + return { + lastMessage, + lastMessageSentAt, + latestReportableMessage, + } + }, [ + _, + convo.lastMessage, + convo.lastReaction, + currentAccount?.did, + isDeletedAccount, + convo.members, + ]) - const [_showActions, setShowActions] = useState(false) + const [showActions, setShowActions] = useState(false) const onMouseEnter = useCallback(() => { setShowActions(true) @@ -331,14 +349,15 @@ function ChatListItemReady({ a.absolute, {top: tokens.space.md, left: tokens.space.lg}, ]}> - - + - {/* + {showMenu && ( - )}*/} - {/**/} + /> diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx index 1d722e9846..4437a9a43f 100644 --- a/src/screens/Messages/components/RequestButtons.tsx +++ b/src/screens/Messages/components/RequestButtons.tsx @@ -2,8 +2,10 @@ import {useCallback} from 'react' import {type ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' import {msg, Trans} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {StackActions, useNavigation} from '@react-navigation/native' import {useQueryClient} from '@tanstack/react-query' +import {type NavigationProp} from '#/lib/routes/types' import {useProfileShadow} from '#/state/cache/profile-shadow' import {useEmail} from '#/state/email-verification' import {useAcceptConversation} from '#/state/queries/messages/accept-conversation' @@ -48,11 +50,11 @@ export function RejectMenu({ }) { const {_} = useLingui() const shadowedProfile = useProfileShadow(profile) - // const navigation = useNavigation() + const navigation = useNavigation() const {mutate: leaveConvo} = useLeaveConvo(convo.id, { onMutate: () => { if (currentScreen === 'conversation') { - // navigation.dispatch(StackActions.pop()) + navigation.dispatch(StackActions.pop()) } }, onError: () => { @@ -190,7 +192,7 @@ export function AcceptChatButton({ }) { const {_} = useLingui() const queryClient = useQueryClient() - // const navigation = useNavigation() + const navigation = useNavigation() const {needsEmailVerification} = useEmail() const emailDialogControl = useEmailDialogControl() @@ -199,10 +201,10 @@ export function AcceptChatButton({ onAcceptConvo?.() if (currentScreen === 'list') { precacheConvoQuery(queryClient, {...convo, status: 'accepted'}) - // navigation.navigate('MessagesConversation', { - // conversation: convo.id, - // accept: true, - // }) + navigation.navigate('MessagesConversation', { + conversation: convo.id, + accept: true, + }) } }, onError: () => { @@ -270,12 +272,12 @@ export function DeleteChatButton({ currentScreen: 'list' | 'conversation' }) { const {_} = useLingui() - // const navigation = useNavigation() + const navigation = useNavigation() const {mutate: leaveConvo} = useLeaveConvo(convo.id, { onMutate: () => { if (currentScreen === 'conversation') { - // navigation.dispatch(StackActions.pop()) + navigation.dispatch(StackActions.pop()) } }, onError: () => { diff --git a/src/screens/Messages/components/RequestListItem.tsx b/src/screens/Messages/components/RequestListItem.tsx index 0276690cc7..1f23b88823 100644 --- a/src/screens/Messages/components/RequestListItem.tsx +++ b/src/screens/Messages/components/RequestListItem.tsx @@ -5,6 +5,7 @@ import {Trans} from '@lingui/macro' import {useModerationOpts} from '#/state/preferences/moderation-opts' import {useSession} from '#/state/session' import {atoms as a, tokens} from '#/alf' +import {KnownFollowers} from '#/components/KnownFollowers' import {Text} from '#/components/Typography' import {ChatListItem, ChatListItemPortal} from './ChatListItem' import {AcceptChatButton, DeleteChatButton, RejectMenu} from './RequestButtons' @@ -26,6 +27,14 @@ export function RequestListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) { return ( + + + {/* spacer, since you can't nest pressables */} {/* Placeholder text so that it responds to the font height */}