From 9755ed98079e64547257badc7a9056c4747c24a0 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Wed, 25 Sep 2024 12:07:47 -0700 Subject: [PATCH 01/10] chat: now that we have blocks, dont ignore archived DMs --- desk/app/chat.hoon | 1 - 1 file changed, 1 deletion(-) diff --git a/desk/app/chat.hoon b/desk/app/chat.hoon index c04ac3a5fe..8311a8bed3 100644 --- a/desk/app/chat.hoon +++ b/desk/app/chat.hoon @@ -1874,7 +1874,6 @@ :: ++ di-take-counter |= =diff:dm:c - ?< =(%archive net.dm) ?< (~(has in blocked) ship) (di-ingest-diff diff) :: From 016e91d3c2df1fbd6f7d2e18a69c77f520637fc6 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 27 Sep 2024 15:57:49 -0700 Subject: [PATCH 02/10] ChatOptionsSheet: icons for notifications sub-menu options, navigation back to root sheet, guard against leaving channel as host Fixes TLON-2737 Fixes TLON-2785 --- .../ui/src/components/ChatOptionsSheet.tsx | 142 ++++++++++-------- 1 file changed, 82 insertions(+), 60 deletions(-) diff --git a/packages/ui/src/components/ChatOptionsSheet.tsx b/packages/ui/src/components/ChatOptionsSheet.tsx index 61cfe519e3..d32d121ad2 100644 --- a/packages/ui/src/components/ChatOptionsSheet.tsx +++ b/packages/ui/src/components/ChatOptionsSheet.tsx @@ -15,9 +15,11 @@ import React, { import { Alert } from 'react-native'; import { useSheet } from 'tamagui'; +import { ChevronLeft } from '../assets/icons'; import { useCalm, useChatOptions, useCurrentUserId } from '../contexts'; import * as utils from '../utils'; import { Action, ActionGroup, ActionSheet } from './ActionSheet'; +import { IconButton } from './IconButton'; import { ListItem } from './ListItem'; export type ChatType = 'group' | db.ChannelType; @@ -163,39 +165,33 @@ export function GroupOptions({ action: () => { handleVolumeUpdate('loud'); }, - icon: currentVolumeLevel === 'loud' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'loud' ? 'Checkmark' : undefined, }, { title: 'Posts, mentions, and replies', action: () => { handleVolumeUpdate('medium'); }, - icon: currentVolumeLevel === 'medium' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'medium' ? 'Checkmark' : undefined, }, { title: 'Only mentions and replies', action: () => { handleVolumeUpdate('soft'); }, - icon: currentVolumeLevel === 'soft' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'soft' ? 'Checkmark' : undefined, }, { title: 'Nothing', action: () => { handleVolumeUpdate('hush'); }, - icon: currentVolumeLevel === 'hush' ? 'Checkmark' : undefined, - }, - { - title: 'Back', - action: () => { - setPane('initial'); - }, + endIcon: currentVolumeLevel === 'hush' ? 'Checkmark' : undefined, }, ], }, ], - [currentVolumeLevel, handleVolumeUpdate, setPane] + [currentVolumeLevel, handleVolumeUpdate] ); const actionGroups = useMemo(() => { @@ -331,9 +327,19 @@ export function GroupOptions({ return ( } + title={pane === 'initial' ? title : 'Notifications for ' + title} + subtitle={ + pane === 'initial' ? subtitle : 'Set what you want to be notified about' + } + icon={ + pane === 'initial' ? ( + + ) : ( + setPane('initial')}> + + + ) + } /> ); } @@ -394,6 +400,11 @@ export function ChannelOptions({ const { onPressChannelMembers, onPressChannelMeta, onPressManageChannels } = useChatOptions() ?? {}; + const currentUserIsHost = useMemo( + () => group?.currentUserIsHost ?? false, + [group?.currentUserIsHost] + ); + const currentUserIsAdmin = useMemo( () => group?.members?.some( @@ -449,39 +460,33 @@ export function ChannelOptions({ action: () => { handleVolumeUpdate('loud'); }, - icon: currentVolumeLevel === 'loud' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'loud' ? 'Checkmark' : undefined, }, { title: 'Posts, mentions, and replies', action: () => { handleVolumeUpdate('medium'); }, - icon: currentVolumeLevel === 'medium' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'medium' ? 'Checkmark' : undefined, }, { title: 'Only mentions and replies', action: () => { handleVolumeUpdate('soft'); }, - icon: currentVolumeLevel === 'soft' ? 'Checkmark' : undefined, + endIcon: currentVolumeLevel === 'soft' ? 'Checkmark' : undefined, }, { title: 'Nothing', action: () => { handleVolumeUpdate('hush'); }, - icon: currentVolumeLevel === 'hush' ? 'Checkmark' : undefined, - }, - { - title: 'Back', - action: () => { - setPane('initial'); - }, + endIcon: currentVolumeLevel === 'hush' ? 'Checkmark' : undefined, }, ], }, ], - [currentVolumeLevel, handleVolumeUpdate, setPane] + [currentVolumeLevel, handleVolumeUpdate] ); const actionGroups: ActionGroup[] = useMemo(() => { @@ -491,6 +496,7 @@ export function ChannelOptions({ actions: [ { title: 'Notifications', + endIcon: 'ChevronRight', action: () => { if (!channel) { return; @@ -501,7 +507,7 @@ export function ChannelOptions({ }, { title: channel?.pin ? 'Unpin' : 'Pin', - icon: 'Pin', + endIcon: 'Pin', action: () => { if (!channel) { return; @@ -573,38 +579,43 @@ export function ChannelOptions({ } as ActionGroup, ] : []), - { - accent: 'negative', - actions: [ - { - title: `Leave chat`, - action: () => { - if (!channel) { - return; - } - Alert.alert( - `Leave ${title}?`, - 'This chat will be removed from list', - [ - { - text: 'Cancel', - onPress: () => console.log('Cancel Pressed'), - style: 'cancel', - }, - { - text: 'Leave', - style: 'destructive', - onPress: () => { - sheetRef.current.setOpen(false); - store.respondToDMInvite({ channel, accept: false }); - }, + ...(!currentUserIsHost + ? [ + { + accent: 'negative', + actions: [ + { + title: `Leave`, + endIcon: 'LogOut', + action: () => { + if (!channel) { + return; + } + Alert.alert( + `Leave ${title}?`, + 'This will be removed from the list', + [ + { + text: 'Cancel', + onPress: () => console.log('Cancel Pressed'), + style: 'cancel', + }, + { + text: 'Leave', + style: 'destructive', + onPress: () => { + sheetRef.current.setOpen(false); + store.respondToDMInvite({ channel, accept: false }); + }, + }, + ] + ); }, - ] - ); - }, - }, - ], - }, + }, + ], + } as ActionGroup, + ] + : []), ]; }, [ channel, @@ -613,15 +624,26 @@ export function ChannelOptions({ setPane, title, currentUserIsAdmin, + currentUserIsHost, group, onPressManageChannels, ]); return ( } + title={pane === 'initial' ? title : 'Notifications for ' + title} + subtitle={ + pane === 'initial' ? subtitle : 'Set what you want to be notified about' + } + icon={ + pane === 'initial' ? ( + + ) : ( + setPane('initial')}> + + + ) + } /> ); } From 1448c21a7a09a133c86ff1ce2fba23123ed948e6 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 27 Sep 2024 16:16:48 -0700 Subject: [PATCH 03/10] ActionSheet: add accent to individual actions; ChatOptionsSheet: add accents for active volume selections --- packages/ui/src/components/ActionSheet.tsx | 23 +++++++++++++++---- .../ui/src/components/ChatOptionsSheet.tsx | 8 +++++++ 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/packages/ui/src/components/ActionSheet.tsx b/packages/ui/src/components/ActionSheet.tsx index 1afb8ba41b..4973c95315 100644 --- a/packages/ui/src/components/ActionSheet.tsx +++ b/packages/ui/src/components/ActionSheet.tsx @@ -35,6 +35,7 @@ export type Action = { render?: (props: ActionRenderProps) => ReactElement; endIcon?: IconType | ReactElement; startIcon?: IconType | ReactElement; + accent?: Accent; }; export type ActionRenderProps = { @@ -296,16 +297,19 @@ const ActionSheetActionFrame = styled(ListItem, { variants: { type: { positive: { + backgroundColor: '$positiveBackground', pressStyle: { backgroundColor: '$positiveBackground', }, }, negative: { + backgroundColor: '$negativeBackground', pressStyle: { backgroundColor: '$negativeBackground', }, }, neutral: {}, + disabled: {}, }, } as const, }); @@ -349,11 +353,15 @@ function ActionSheetAction({ action }: { action: Action }) { action.render({ action }) ) : ( - {action.startIcon && resolveIcon(action.startIcon)} + {action.startIcon && + resolveIcon(action.startIcon, action.accent ?? accent)} - {action.title} + + {action.title} + {action.description && ( {action.description} @@ -361,15 +369,17 @@ function ActionSheetAction({ action }: { action: Action }) { )} {action.endIcon && ( - {resolveIcon(action.endIcon)} + + {resolveIcon(action.endIcon, action.accent ?? accent)} + )} ); } -function resolveIcon(icon: IconType | ReactElement) { +function resolveIcon(icon: IconType | ReactElement, accent: Accent) { if (typeof icon === 'string') { - return ; + return ; } return icon; } @@ -386,6 +396,9 @@ const ActionSheetActionIcon = styled(Icon, { color: '$negativeActionText', }, neutral: {}, + disabled: { + color: '$tertiaryText', + }, }, } as const, }); diff --git a/packages/ui/src/components/ChatOptionsSheet.tsx b/packages/ui/src/components/ChatOptionsSheet.tsx index d32d121ad2..fb6c274099 100644 --- a/packages/ui/src/components/ChatOptionsSheet.tsx +++ b/packages/ui/src/components/ChatOptionsSheet.tsx @@ -162,6 +162,7 @@ export function GroupOptions({ actions: [ { title: 'All activity', + accent: currentVolumeLevel === 'loud' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('loud'); }, @@ -169,6 +170,7 @@ export function GroupOptions({ }, { title: 'Posts, mentions, and replies', + accent: currentVolumeLevel === 'medium' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('medium'); }, @@ -176,6 +178,7 @@ export function GroupOptions({ }, { title: 'Only mentions and replies', + accent: currentVolumeLevel === 'soft' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('soft'); }, @@ -183,6 +186,7 @@ export function GroupOptions({ }, { title: 'Nothing', + accent: currentVolumeLevel === 'hush' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('hush'); }, @@ -457,6 +461,7 @@ export function ChannelOptions({ actions: [ { title: 'All activity', + accent: currentVolumeLevel === 'loud' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('loud'); }, @@ -464,6 +469,7 @@ export function ChannelOptions({ }, { title: 'Posts, mentions, and replies', + accent: currentVolumeLevel === 'medium' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('medium'); }, @@ -471,6 +477,7 @@ export function ChannelOptions({ }, { title: 'Only mentions and replies', + accent: currentVolumeLevel === 'soft' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('soft'); }, @@ -478,6 +485,7 @@ export function ChannelOptions({ }, { title: 'Nothing', + accent: currentVolumeLevel === 'hush' ? 'positive' : 'neutral', action: () => { handleVolumeUpdate('hush'); }, From fe8ba1a577606dbee91f9df8060e207bf05eef9a Mon Sep 17 00:00:00 2001 From: James Acklin Date: Fri, 27 Sep 2024 17:05:52 -0700 Subject: [PATCH 04/10] ChannelScreen: fix tsc error for CI --- packages/app/features/top/ChannelScreen.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/app/features/top/ChannelScreen.tsx b/packages/app/features/top/ChannelScreen.tsx index 51b3d9217e..20638c65b5 100644 --- a/packages/app/features/top/ChannelScreen.tsx +++ b/packages/app/features/top/ChannelScreen.tsx @@ -1,3 +1,5 @@ +import { useFocusEffect } from '@react-navigation/native'; +import { useIsFocused } from '@react-navigation/native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { createDevLogger } from '@tloncorp/shared/dist'; import * as db from '@tloncorp/shared/dist/db'; @@ -22,9 +24,7 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { useChannelContext } from '../../hooks/useChannelContext'; import { useChannelNavigation } from '../../hooks/useChannelNavigation'; import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation'; -import { useFocusEffect } from '@react-navigation/native'; import { useGroupActions } from '../../hooks/useGroupActions'; -import { useIsFocused } from '@react-navigation/native'; import type { RootStackParamList } from '../../navigation/types'; const logger = createDevLogger('ChannelScreen', false); @@ -119,7 +119,8 @@ export default function ChannelScreen(props: Props) { // // ------------------------| syncedAt // session.startTime |--------------- - if (syncedAt >= session.startTime) { + // NOTE: inserted a guard to prevent session.startTime from being undefined + if (syncedAt >= session.startTime!) { return true; } From 8041f0a8ae5f9c8d30b3aa3c22a2433b954075ce Mon Sep 17 00:00:00 2001 From: James Acklin Date: Mon, 30 Sep 2024 13:59:26 -0400 Subject: [PATCH 05/10] ChatList: remove native scroll events and title-swapping --- packages/ui/src/components/ChatList.tsx | 36 ------------------------- 1 file changed, 36 deletions(-) diff --git a/packages/ui/src/components/ChatList.tsx b/packages/ui/src/components/ChatList.tsx index d90b7fa974..e0e3bfba57 100644 --- a/packages/ui/src/components/ChatList.tsx +++ b/packages/ui/src/components/ChatList.tsx @@ -8,20 +8,16 @@ import { useEffect, useLayoutEffect, useMemo, - useRef, useState, } from 'react'; import React from 'react'; import { LayoutChangeEvent, - NativeScrollEvent, - NativeSyntheticEvent, SectionList, SectionListData, SectionListRenderItemInfo, StyleProp, ViewStyle, - ViewToken, } from 'react-native'; import Animated, { Easing, @@ -55,7 +51,6 @@ function ChatListComponent({ onLongPressItem, onPressItem, onPressMenuButton, - onSectionChange, activeTab, setActiveTab, showFilters, @@ -126,35 +121,6 @@ function ChatListComponent({ [] ); - const isAtTopRef = useRef(true); - - const onViewableItemsChanged = useRef( - ({ viewableItems }: { viewableItems: ViewToken[] }) => { - if (viewableItems.length === 0) { - return; - } - - if (!isAtTopRef.current) { - const { section } = viewableItems[0]; - if (section) { - onSectionChange?.(section.title); - } - } - } - ).current; - - const handleScroll = useRef( - (event: NativeSyntheticEvent) => { - const atTop = event.nativeEvent.contentOffset.y === 0; - if (atTop !== isAtTopRef.current) { - isAtTopRef.current = atTop; - if (atTop) { - onSectionChange?.('Home'); - } - } - } - ).current; - const handlePressTryAll = useCallback(() => { setActiveTab('all'); }, [setActiveTab]); @@ -194,8 +160,6 @@ function ChatListComponent({ waitForInteraction: false, }} renderSectionHeader={renderSectionHeader} - onViewableItemsChanged={onViewableItemsChanged} - onMomentumScrollEnd={activeTab === 'all' ? handleScroll : undefined} /> )} From cae675d01d83b3dbaa5bbdd708a004c2210c954f Mon Sep 17 00:00:00 2001 From: Patrick O'Sullivan Date: Mon, 30 Sep 2024 14:04:59 -0500 Subject: [PATCH 06/10] input: take keyboard height into account when setting maxInputHeight --- .../components/MessageInput/index.native.tsx | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/ui/src/components/MessageInput/index.native.tsx b/packages/ui/src/components/MessageInput/index.native.tsx index bc87fd0e64..5f283f9458 100644 --- a/packages/ui/src/components/MessageInput/index.native.tsx +++ b/packages/ui/src/components/MessageInput/index.native.tsx @@ -163,7 +163,7 @@ export const MessageInput = forwardRef( const titleInputHeight = 48; const inputBasePadding = getToken('$s', 'space'); const imageInputButtonHeight = 50; - const maxInputHeight = useMemo( + const maxInputHeightBasic = useMemo( () => height - headerHeight - bottom - top, [height, bottom, top, headerHeight] ); @@ -176,6 +176,7 @@ export const MessageInput = forwardRef( [height, basicOffset, bottom, inputBasePadding] ); const [bigInputHeight, setBigInputHeight] = useState(bigInputHeightBasic); + const [maxInputHeight, setMaxInputHeight] = useState(maxInputHeightBasic); const [mentionText, setMentionText] = useState(); const [showMentionPopup, setShowMentionPopup] = useState(false); @@ -747,7 +748,7 @@ export const MessageInput = forwardRef( if (payload === containerHeight) { return; } - if (containerHeight > maxInputHeight) { + if (containerHeight > maxInputHeightBasic) { return; } setContainerHeight(payload); @@ -791,7 +792,7 @@ export const MessageInput = forwardRef( editorCrashed, setEditorCrashed, containerHeight, - maxInputHeight, + maxInputHeightBasic, ] ); @@ -812,7 +813,18 @@ export const MessageInput = forwardRef( setBigInputHeight(bigInputHeightBasic); }); } - }, [bigInput, bigInputHeightBasic]); + + if (!bigInput) { + Keyboard.addListener('keyboardDidShow', () => { + const keyboardHeight = Keyboard.metrics()?.height || 300; + setMaxInputHeight(maxInputHeightBasic - keyboardHeight); + }); + + Keyboard.addListener('keyboardDidHide', () => { + setMaxInputHeight(maxInputHeightBasic); + }); + } + }, [bigInput, bigInputHeightBasic, maxInputHeightBasic]); // we need to check if the app within the webview actually loaded useEffect(() => { From 1a4ecd10214829a122a894b1deb24ac9b0a96003 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Mon, 30 Sep 2024 16:07:22 -0400 Subject: [PATCH 07/10] Revert "ChannelScreen: fix tsc error for CI" This reverts commit fe8ba1a577606dbee91f9df8060e207bf05eef9a. --- packages/app/features/top/ChannelScreen.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/app/features/top/ChannelScreen.tsx b/packages/app/features/top/ChannelScreen.tsx index 20638c65b5..51b3d9217e 100644 --- a/packages/app/features/top/ChannelScreen.tsx +++ b/packages/app/features/top/ChannelScreen.tsx @@ -1,5 +1,3 @@ -import { useFocusEffect } from '@react-navigation/native'; -import { useIsFocused } from '@react-navigation/native'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import { createDevLogger } from '@tloncorp/shared/dist'; import * as db from '@tloncorp/shared/dist/db'; @@ -24,7 +22,9 @@ import React, { useCallback, useEffect, useMemo } from 'react'; import { useChannelContext } from '../../hooks/useChannelContext'; import { useChannelNavigation } from '../../hooks/useChannelNavigation'; import { useChatSettingsNavigation } from '../../hooks/useChatSettingsNavigation'; +import { useFocusEffect } from '@react-navigation/native'; import { useGroupActions } from '../../hooks/useGroupActions'; +import { useIsFocused } from '@react-navigation/native'; import type { RootStackParamList } from '../../navigation/types'; const logger = createDevLogger('ChannelScreen', false); @@ -119,8 +119,7 @@ export default function ChannelScreen(props: Props) { // // ------------------------| syncedAt // session.startTime |--------------- - // NOTE: inserted a guard to prevent session.startTime from being undefined - if (syncedAt >= session.startTime!) { + if (syncedAt >= session.startTime) { return true; } From e4c7e2c400a3165501dd21cde15f6e671e519bd2 Mon Sep 17 00:00:00 2001 From: Hunter Miller Date: Mon, 30 Sep 2024 14:36:53 -0500 Subject: [PATCH 08/10] grouper: fix private group invites --- desk/app/grouper.hoon | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desk/app/grouper.hoon b/desk/app/grouper.hoon index 50819467fa..394f13c088 100644 --- a/desk/app/grouper.hoon +++ b/desk/app/grouper.hoon @@ -172,7 +172,7 @@ %shut ~? dev-mode ['inviting to private/secret' joiner.bite] =/ =action:groups - :- [our.bowl token.bite] + :- flag :- now.bowl :- %cordon [%shut [%add-ships %pending (~(gas in *(set ship)) ~[joiner.bite])]] From ffb5aa7e1e3d181a9a98596f8e646b4e6c67de55 Mon Sep 17 00:00:00 2001 From: James Acklin Date: Mon, 30 Sep 2024 18:34:15 -0400 Subject: [PATCH 09/10] useConnectionStatus: always return 'yes' for self --- .../app/features/top/useConnectionStatus.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/app/features/top/useConnectionStatus.tsx b/packages/app/features/top/useConnectionStatus.tsx index f0033e2e28..e772ed7eb6 100644 --- a/packages/app/features/top/useConnectionStatus.tsx +++ b/packages/app/features/top/useConnectionStatus.tsx @@ -1,17 +1,23 @@ import * as api from '@tloncorp/shared/dist/api'; -import { debounce } from 'lodash'; import { ConnectionStatus } from '@tloncorp/shared/dist/api'; +import { debounce } from 'lodash'; +import { useCurrentUserId } from 'packages/ui/src'; import { useEffect, useState } from 'react'; export const useConnectionStatus = (contactId: string) => { const [connectionStatus, setConnectionStatus] = useState(null); + const currentUserId = useCurrentUserId(); useEffect(() => { - api.checkConnectionStatus( - contactId, - debounce(setConnectionStatus, 500, { trailing: true }) - ); - }, [contactId]); + if (currentUserId === contactId) { + setConnectionStatus({ status: 'yes', complete: true }); + } else { + api.checkConnectionStatus( + contactId, + debounce(setConnectionStatus, 500, { trailing: true }) + ); + } + }, [contactId, currentUserId]); return connectionStatus; }; From a7f1ab6c3187b13df892eaf9bf35f9a85ddd809b Mon Sep 17 00:00:00 2001 From: ~latter-bolden Date: Mon, 30 Sep 2024 15:47:33 -0700 Subject: [PATCH 10/10] fix broken import --- packages/app/features/top/useConnectionStatus.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/features/top/useConnectionStatus.tsx b/packages/app/features/top/useConnectionStatus.tsx index e772ed7eb6..5a4c842ac1 100644 --- a/packages/app/features/top/useConnectionStatus.tsx +++ b/packages/app/features/top/useConnectionStatus.tsx @@ -1,7 +1,7 @@ import * as api from '@tloncorp/shared/dist/api'; import { ConnectionStatus } from '@tloncorp/shared/dist/api'; +import { useCurrentUserId } from '@tloncorp/ui'; import { debounce } from 'lodash'; -import { useCurrentUserId } from 'packages/ui/src'; import { useEffect, useState } from 'react'; export const useConnectionStatus = (contactId: string) => {