Skip to content

Commit b8ff863

Browse files
Merge upstream develop
2 parents 3d56fd0 + efd9863 commit b8ff863

19 files changed

Lines changed: 243 additions & 555 deletions

File tree

File renamed without changes.
File renamed without changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from '@/src/features/profile/ProfileScreen';

apps/polycentric/src/common/components/layout/nav/VerticalNav.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import { Atoms, useTheme } from '@/src/common/theme';
2-
import { useRoutePath } from '@react-navigation/native';
3-
import { ComponentProps, useMemo } from 'react';
2+
import { ComponentProps } from 'react';
43

54
import { NavItem } from './NavItem';
65
import { View } from 'react-native';
7-
import {
8-
publicKeyToString,
9-
publicKeyToStringURLSafe,
10-
useCurrentIdentity,
11-
} from '@/src/common/lib/polycentric-hooks';
6+
import { useCurrentIdentity } from '@/src/common/lib/polycentric-hooks';
127
import { Ionicons } from '@expo/vector-icons';
138

149
type VerticalNavProps = {
@@ -19,8 +14,6 @@ export function VerticalNav({ style }: VerticalNavProps) {
1914
const theme = useTheme();
2015
const { identity } = useCurrentIdentity();
2116

22-
const publicKeySafe = identity?.identityKey;
23-
2417
return (
2518
<View
2619
style={[Atoms.py_xs, Atoms.flex_1, Atoms.flex_col, Atoms.gap_sm, style]}
@@ -30,13 +23,13 @@ export function VerticalNav({ style }: VerticalNavProps) {
3023
icon={<Ionicons name="home-outline" size={24} />}
3124
href="/feed"
3225
/>
33-
{publicKeySafe && (
26+
{identity?.identityKey && (
3427
<NavItem
3528
label="Profile"
3629
icon={<Ionicons name="person-outline" size={24} />}
3730
href={{
38-
pathname: '/profile/[publicKey]',
39-
params: { publicKey: publicKeySafe },
31+
pathname: '/[identityId]',
32+
params: { identityId: identity?.identityKey },
4033
}}
4134
/>
4235
)}

apps/polycentric/src/common/constants/routes.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export function openCompose(replyToPostId?: string) {
1010
}
1111
}
1212

13-
// TODO: abandon systemKey terminology in lib and apps. just use publicKey
1413
export const Routes = {
1514
tabs: {
1615
feed: {
@@ -20,11 +19,9 @@ export const Routes = {
2019
},
2120
search: '/search',
2221
claims: '/claims',
23-
profile: (publicKey: string) => `/profile/${publicKey}` as const,
24-
post: Object.assign((postId: string) => `/post/${postId}` as const, {
25-
reply: (postId: string, replyTo: string) =>
26-
`/post/${postId}?replyTo=${encodeURIComponent(replyTo)}` as const,
27-
}),
22+
profile: (identityId: string) => `/${identityId}` as const,
23+
post: (identityId: string, postId: string) =>
24+
`/${identityId}/post/${postId}` as const,
2825
settings: {
2926
index: '/settings',
3027
identity: '/settings/identity',

apps/polycentric/src/common/lib/polycentric-hooks/helpers.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,13 @@ export function parsePostId(postId: string): {
7979
}
8080
}
8181

82+
/** Extract the sequence number (logicalClock) from an internal eventKey postId. */
83+
export function postIdToSequence(postId: string): string | null {
84+
const parts = postId.split(':');
85+
if (parts.length !== 3) return null;
86+
return parts[2];
87+
}
88+
8289
export function decodePostEvent(
8390
signedEvent: types.SignedEvent,
8491
): PostData | null {

apps/polycentric/src/common/lib/polycentric-hooks/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export {
1717
} from './PolycentricProvider';
1818

1919
// Post page (reply feed + parent list)
20-
export { usePostPage, useNavigateToParentPost } from './useConversation';
20+
export { usePostPage } from './useConversation';
2121

2222
// Action hooks
2323
export {
@@ -48,6 +48,7 @@ export {
4848
bytesToHex,
4949
hexToBytes,
5050
eventKey,
51+
postIdToSequence,
5152
truncateName,
5253
publicKeyToString,
5354
stringToPublicKey,

apps/polycentric/src/common/lib/polycentric-hooks/useConversation.ts

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { useCallback, useEffect, useRef } from 'react';
22
import { useFocusEffect } from 'expo-router';
3-
import { types } from '@polycentric/react-native';
4-
import { usePolycentricContext, usePolycentric } from './PolycentricProvider';
5-
import { decodePostEvent, eventKey } from './helpers';
3+
import { usePolycentricContext } from './PolycentricProvider';
64
import { useStore } from './store';
75

86
const REPLIES_FEED_PREFIX = 'replies:';
@@ -50,39 +48,3 @@ export function usePostPage(postId: string) {
5048

5149
return { postId, replyIds, isLoading, reload };
5250
}
53-
54-
/** Callback to resolve parent post (cache or fetch) and call onPostPress(postId). */
55-
export function useNavigateToParentPost(onPostPress: (postId: string) => void) {
56-
const client = usePolycentric();
57-
const { store } = usePolycentricContext();
58-
59-
return useCallback(
60-
async (postId: string) => {
61-
const post = store.getState().posts[postId];
62-
if (!post) return;
63-
const { decoded } = post;
64-
if (
65-
!decoded.parentAuthorPublicKey?.key ||
66-
!decoded.parentProcess?.process ||
67-
decoded.parentLogicalClock == null
68-
)
69-
return;
70-
71-
const parentId = eventKey(
72-
decoded.parentAuthorPublicKey.key,
73-
decoded.parentProcess.process,
74-
decoded.parentLogicalClock,
75-
);
76-
77-
const cached = store.getState().posts[parentId];
78-
if (cached) {
79-
onPostPress(parentId);
80-
return;
81-
}
82-
83-
// TODO: queryAuthorFeed requires queryManager which is not yet in v2
84-
onPostPress(parentId);
85-
},
86-
[client, store, onPostPress],
87-
);
88-
}

apps/polycentric/src/common/lib/polycentric-hooks/useProfileScreenData.ts

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,7 @@ import {
99
useLikesFeed,
1010
useFollowStatus,
1111
} from './PolycentricProvider';
12-
import {
13-
getIdentityId,
14-
identiconUrl,
15-
shortenIdentityId,
16-
stringURLSafeToPublicKey,
17-
} from './helpers';
12+
import { getIdentityId, identiconUrl, shortenIdentityId } from './helpers';
1813
import { useStore } from './store';
1914

2015
export type ProfileScreenData = {
@@ -35,19 +30,33 @@ export type ProfileScreenData = {
3530
};
3631

3732
export function useProfileScreenData(
38-
publicKeyParam: string | undefined,
33+
identityIdParam: string | undefined,
3934
options?: { getIsAborted?: () => boolean },
4035
): ProfileScreenData {
41-
const publicKey = useMemo(
42-
() =>
43-
publicKeyParam
44-
? stringURLSafeToPublicKey(publicKeyParam)
45-
: types.PublicKey.create(),
46-
[publicKeyParam],
47-
);
36+
const { identity: selfIdentity, publicKey: selfPublicKey } =
37+
useCurrentIdentity();
38+
39+
// Resolve identityId → publicKey. For self we use the current identity's
40+
// public key. Otherwise scan known posts for a matching authorIdentity.
41+
const { store } = usePolycentricContext();
42+
const knownPublicKey = useStore(store, (state) => {
43+
if (!identityIdParam) return null;
44+
for (const post of Object.values(state.posts)) {
45+
if (post.decoded.authorIdentity === identityIdParam) {
46+
return post.decoded.authorPublicKey;
47+
}
48+
}
49+
return null;
50+
});
51+
52+
const isSelf =
53+
!!identityIdParam && selfIdentity?.identityKey === identityIdParam;
54+
55+
const publicKey = useMemo(() => {
56+
if (isSelf && selfPublicKey) return selfPublicKey;
57+
return knownPublicKey ?? types.PublicKey.create();
58+
}, [isSelf, selfPublicKey, knownPublicKey]);
4859

49-
const { isCurrentIdentity, identity: selfIdentity } = useCurrentIdentity();
50-
const isSelf = isCurrentIdentity(publicKey);
5160
const getIsAborted = options?.getIsAborted;
5261

5362
const username = useUsername(publicKey);
@@ -56,23 +65,7 @@ export function useProfileScreenData(
5665
const likesFeed = useLikesFeed({ enabled: isSelf, getIsAborted });
5766
const followStatus = useFollowStatus(publicKey);
5867

59-
// Resolve an identity id for this profile. For self we already know it
60-
// from the client; for other users we scan locally-known posts for the
61-
// first one signed by this public key.
62-
const { store } = usePolycentricContext();
63-
const postIdentity = useStore(store, (state) => {
64-
for (const post of Object.values(state.posts)) {
65-
const key = post.decoded.authorPublicKey.key;
66-
if (key && bytesEqual(key, publicKey.key ?? new Uint8Array())) {
67-
return post.decoded.authorIdentity ?? null;
68-
}
69-
}
70-
return null;
71-
});
72-
73-
const identityKey = isSelf
74-
? (selfIdentity?.identityKey ?? null)
75-
: postIdentity;
68+
const identityKey = identityIdParam ?? null;
7669

7770
const short = identityKey
7871
? shortenIdentityId(identityKey)
@@ -99,11 +92,3 @@ export function useProfileScreenData(
9992
setActiveFeed,
10093
};
10194
}
102-
103-
function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
104-
if (a.length !== b.length) return false;
105-
for (let i = 0; i < a.length; i++) {
106-
if (a[i] !== b[i]) return false;
107-
}
108-
return true;
109-
}

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

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Routes } from '@/src/common/constants';
22
import {
33
decodePostEvent,
4-
publicKeyToStringURLSafe,
4+
postIdToSequence,
55
useCurrentIdentity,
66
usePolycentricContext,
77
useStore,
@@ -14,7 +14,8 @@ import { ComposeSheetInner } from './ComposeSheetInner';
1414

1515
export default function ComposeSheetRoute() {
1616
const { store } = usePolycentricContext();
17-
const { publicKey: myPublicKey } = useCurrentIdentity();
17+
const { identity: selfIdentity } = useCurrentIdentity();
18+
const myIdentityId = selfIdentity?.identityKey ?? null;
1819
const params = useLocalSearchParams<{ replyTo?: string }>();
1920
const replyToPostId = params.replyTo;
2021

@@ -31,19 +32,22 @@ export default function ComposeSheetRoute() {
3132
const handlePostCreated = useCallback(
3233
async (signedEvent: types.SignedEvent) => {
3334
const decoded = decodePostEvent(signedEvent);
34-
if (decoded) {
35+
if (decoded && decoded.authorIdentity) {
3536
store.getState().ingestPost(decoded.id, signedEvent, decoded);
36-
router.replace(Routes.tabs.post(decoded.id));
37+
const sequence = postIdToSequence(decoded.id);
38+
if (sequence) {
39+
router.replace(Routes.tabs.post(decoded.authorIdentity, sequence));
40+
}
3741
}
3842
},
3943
[store],
4044
);
4145

4246
const handleAvatarPress = useCallback(() => {
43-
if (myPublicKey) {
44-
router.push(Routes.tabs.profile(publicKeyToStringURLSafe(myPublicKey)));
47+
if (myIdentityId) {
48+
router.push(Routes.tabs.profile(myIdentityId));
4549
}
46-
}, [myPublicKey]);
50+
}, [myIdentityId]);
4751

4852
return (
4953
<SheetMenu onClose={() => router.back()} detents={[0.82]} scrollable>

0 commit comments

Comments
 (0)