Skip to content

Commit fb0b310

Browse files
Merge upstream develop
2 parents 2c9bdd9 + db5901b commit fb0b310

12 files changed

Lines changed: 137 additions & 50 deletions

File tree

apps/polycentric/app/(tabs)/_layout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ export default function TabsLayout() {
3535
<NativeTabs.Trigger.Icon sf="bell" md="notifications" />
3636
</NativeTabs.Trigger>
3737

38-
<NativeTabs.Trigger name="trust">
39-
<NativeTabs.Trigger.Label>Trust</NativeTabs.Trigger.Label>
38+
<NativeTabs.Trigger name="verify">
39+
<NativeTabs.Trigger.Label>Verify</NativeTabs.Trigger.Label>
4040
<NativeTabs.Trigger.Icon sf="checkmark.seal" md="verified" />
4141
</NativeTabs.Trigger>
4242

apps/polycentric/app/(tabs)/trust.tsx

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from '@/src/features/verify/VerifyScreen';

apps/polycentric/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"@expo/metro-runtime": "~55.0.11",
2121
"@expo/vector-icons": "^15.0.3",
2222
"@gorhom/bottom-sheet": "^5.2.8",
23-
"@lodev09/react-native-true-sheet": "^3.0.4",
23+
"@lodev09/react-native-true-sheet": "^3.10.1",
2424
"@noble/hashes": "catalog:",
2525
"@polycentric/react-native": "workspace:*",
2626
"@react-native-async-storage/async-storage": "^2.2.0",

apps/polycentric/src/common/components/Icon.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export const IconsMap = {
6161
time: defineIcon(Ionicons, 'time-outline'),
6262
trash: defineIcon(Ionicons, 'trash-outline'),
6363
trashBin: defineIcon(Ionicons, 'trash-bin'),
64+
verify: defineIcon(MaterialCommunityIcons, 'check-decagram-outline'),
6465
};
6566

6667
export type IconName = keyof typeof IconsMap;

apps/polycentric/src/common/components/sheet/Sheet.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type CommonProps = {
4646
header?: ReactElement;
4747
/** Pinned footer element — bottom of the sheet (native) / card (web). */
4848
footer?: ReactElement;
49+
/** Fired once the sheet has finished presenting. */
50+
onPresented?: () => void;
4951
};
5052

5153
export type SheetProps =
@@ -209,6 +211,7 @@ function NativeSheet({
209211
initialDetentIndex={0}
210212
dismissible={dismissible}
211213
scrollable={scrollable}
214+
onDidPresent={() => props.onPresented?.()}
212215
onDidDismiss={() => {
213216
if (suppressOnCloseRef.current) {
214217
suppressOnCloseRef.current = false;
@@ -269,6 +272,7 @@ function WebModal({
269272
navigation,
270273
header,
271274
footer,
275+
onPresented,
272276
}: WebInternalProps) {
273277
const { theme } = useTheme();
274278
const { width } = useWindowDimensions();
@@ -277,6 +281,12 @@ function WebModal({
277281
const isInline = open !== undefined;
278282
const { animatedStyle, fadeOut, isExiting } = useFadeTransition();
279283

284+
// Mirror native's `onPresented` once the modal has mounted.
285+
useEffect(() => {
286+
onPresented?.();
287+
// eslint-disable-next-line react-hooks/exhaustive-deps
288+
}, []);
289+
280290
const close = useCallback(() => {
281291
if (isInline) fadeOut(() => onClose?.());
282292
else if (router.canGoBack()) router.back();

apps/polycentric/src/features/composer/ComposeSheet.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Atoms, useTheme } from '@/src/common/theme';
55
import { isWeb } from '@/src/common/util/platform';
66
import { types } from '@polycentric/react-native';
77
import { router } from 'expo-router';
8-
import { useCallback } from 'react';
8+
import { useCallback, useState } from 'react';
99
import { ActivityIndicator } from 'react-native';
1010
import { ComposeSheetFooterBar } from './ComposeSheetFooterBar';
1111
import { ComposerFields } from './ComposerFields';
@@ -41,10 +41,14 @@ export function ComposeSheet({
4141
onClose,
4242
});
4343

44+
// Focus the field only after the sheet has presented (iOS fix)
45+
const [autoFocus, setAutoFocus] = useState(false);
46+
4447
return (
4548
<Sheet
4649
detents={[1]}
4750
scrollable
51+
onPresented={() => setAutoFocus(true)}
4852
footer={
4953
<ComposeSheetFooterBar
5054
variant={isWeb ? 'web' : 'native'}
@@ -101,6 +105,7 @@ export function ComposeSheet({
101105
attachments={composer.attachments}
102106
submitting={composer.submitting}
103107
onRemoveAttachment={composer.handleRemoveAttachment}
108+
autoFocus={autoFocus}
104109
/>
105110
</Sheet.Content>
106111
</Sheet>

apps/polycentric/src/features/composer/ComposerFields.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ type ComposerFieldsProps = {
2626
attachments: Attachment[];
2727
submitting: boolean;
2828
onRemoveAttachment: (id: string) => void;
29+
/** Auto-focus the text field.**/
30+
autoFocus?: boolean;
2931
};
3032

3133
/**
@@ -45,6 +47,7 @@ export function ComposerFields({
4547
attachments,
4648
submitting,
4749
onRemoveAttachment,
50+
autoFocus = true,
4851
}: ComposerFieldsProps) {
4952
const { theme } = useTheme();
5053

@@ -83,9 +86,12 @@ export function ComposerFields({
8386
) : null}
8487
<View style={Atoms.flex_1}>
8588
<TextArea
89+
// `autoFocus` only fires on mount, so re-key when it flips
90+
// (false → true after the sheet presents) to actually focus.
91+
key={autoFocus ? 'autofocus' : 'no-autofocus'}
8692
variant="plain"
8793
placeholder={placeholder}
88-
autoFocus
94+
autoFocus={autoFocus}
8995
value={text}
9096
onChangeText={setText}
9197
// disabled={submitting}

apps/polycentric/src/features/composer/hooks/useComposer.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,12 @@ export function useComposer({
8989
const attachDisabled = submitting || attachments.length >= MAX_ATTACHMENTS;
9090

9191
const handleClose = useCallback(() => {
92-
if (!submitting) onClose();
93-
}, [submitting, onClose]);
92+
if (submitting) return;
93+
onClose();
94+
// Reset here too: the native compose tab stays mounted after closing,
95+
// so the unmount reset below wouldn't fire for it.
96+
resetComposer();
97+
}, [submitting, onClose, resetComposer]);
9498

9599
const handleAttachImage = useCallback(async () => {
96100
if (attachDisabled) return;
@@ -109,13 +113,34 @@ export function useComposer({
109113
height: asset.height,
110114
}));
111115
addAttachments(additions);
112-
}, [attachDisabled, attachments.length, addAttachments]);
116+
setError(null);
117+
}, [attachDisabled, attachments.length, addAttachments, setError]);
113118

114119
const handleRemoveAttachment = useCallback(
115-
(id: string) => removeAttachment(id),
116-
[removeAttachment],
120+
(id: string) => {
121+
removeAttachment(id);
122+
setError(null);
123+
},
124+
[removeAttachment, setError],
125+
);
126+
127+
// Editing the draft (text or images) clears any prior post error.
128+
const handleTextChange = useCallback(
129+
(next: string) => {
130+
setText(next);
131+
setError(null);
132+
},
133+
[setText, setError],
117134
);
118135

136+
// Reset the entire composer (text, attachments, submitting, error) when
137+
// it closes (unmounts), so nothing carries over to the next open.
138+
useEffect(() => {
139+
return () => {
140+
resetComposer();
141+
};
142+
}, [resetComposer]);
143+
119144
// Auto-open the image picker once when the caller requested it
120145
// (e.g. tapping the attach icon in the inline composer).
121146
const attachOnMountFiredRef = useRef(false);
@@ -236,7 +261,7 @@ export function useComposer({
236261
return {
237262
// state
238263
text,
239-
setText,
264+
setText: handleTextChange,
240265
attachments,
241266
submitting,
242267
error,

apps/polycentric/src/features/trust/TrustScreen.tsx

Lines changed: 0 additions & 33 deletions
This file was deleted.

0 commit comments

Comments
 (0)