Skip to content

Commit 7796d46

Browse files
Merge upstream develop
2 parents a88f4f8 + 13e1fbf commit 7796d46

30 files changed

Lines changed: 2672 additions & 68 deletions

File tree

Cargo.lock

Lines changed: 522 additions & 28 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

apps/polycentric/app.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
2323
infoPlist: {
2424
NSCameraUsageDescription: '$(PRODUCT_NAME) needs access to your Camera.',
2525
},
26+
entitlements: {
27+
'aps-environment': 'production',
28+
},
2629
},
2730
android: {
2831
adaptiveIcon: {
@@ -68,6 +71,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
6871
},
6972
],
7073
'expo-image',
74+
'expo-notifications',
7175
],
7276
experiments: {
7377
typedRoutes: true,

apps/polycentric/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
"expo-clipboard": "~55.0.13",
3636
"expo-constants": "~55.0.7",
3737
"expo-dev-client": "~55.0.10",
38+
"expo-device": "~55.0.0",
3839
"expo-document-picker": "~55.0.8",
3940
"expo-font": "~55.0.4",
4041
"expo-haptics": "~55.0.8",
4142
"expo-image": "~55.0.5",
4243
"expo-image-picker": "~55.0.10",
4344
"expo-linking": "~55.0.7",
45+
"expo-notifications": "~55.0.0",
4446
"expo-router": "~55.0.3",
4547
"expo-server": "~55.0.8",
4648
"expo-splash-screen": "~55.0.10",
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import Constants from 'expo-constants';
2+
import * as Device from 'expo-device';
3+
import * as Notifications from 'expo-notifications';
4+
import { Platform } from 'react-native';
5+
6+
export async function registerForPushNotifications(): Promise<string | null> {
7+
if (Platform.OS === 'web' || !Device.isDevice) return null;
8+
9+
if (Platform.OS === 'android') {
10+
await Notifications.setNotificationChannelAsync('default', {
11+
name: 'default',
12+
importance: Notifications.AndroidImportance.DEFAULT,
13+
});
14+
}
15+
16+
const existing = await Notifications.getPermissionsAsync();
17+
let granted = existing.granted;
18+
if (!granted) {
19+
const req = await Notifications.requestPermissionsAsync();
20+
granted = req.granted;
21+
}
22+
if (!granted) return null;
23+
24+
const projectId =
25+
Constants.expoConfig?.extra?.eas?.projectId ??
26+
Constants.easConfig?.projectId;
27+
if (!projectId) {
28+
console.warn('Expo projectId missing — cannot fetch push token');
29+
return null;
30+
}
31+
32+
const token = await Notifications.getExpoPushTokenAsync({ projectId });
33+
return token.data;
34+
}

apps/polycentric/src/common/lib/polycentric-hooks/PolycentricProvider.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { DEFAULT_IDENTITY_NAME } from '@/src/common/constants';
2+
import useFollows from '@/src/features/follow/hooks/useFollows';
23
import {
34
PolycentricClient,
45
createPolycentricClient,
@@ -16,15 +17,14 @@ import {
1617
useState,
1718
type ReactNode,
1819
} from 'react';
19-
import { ActivityIndicator, Platform, Text, View } from 'react-native';
20-
import useFollows from '@/src/features/follow/hooks/useFollows';
21-
import { pubkeyStr } from './helpers';
20+
import { Platform, Text, View } from 'react-native';
21+
import { Atoms, useTheme } from '../../theme';
22+
import { registerForPushNotifications } from '../notifications/registerPushToken';
2223
import {
2324
createPolycentricStore,
2425
useStore,
2526
type PolycentricStoreApi,
2627
} from './store';
27-
import { Atoms, useTheme } from '../../theme';
2828

2929
export interface PolycentricContextValue {
3030
client: PolycentricClient;
@@ -126,6 +126,22 @@ export function PolycentricProvider({
126126
}
127127
}, [isLoading, onInitialized]);
128128

129+
useEffect(() => {
130+
if (!client || !currentIdentity) return;
131+
void (async () => {
132+
try {
133+
const token = await registerForPushNotifications();
134+
if (!token) return;
135+
await client.registerPushNotifications(DEFAULT_SEED_SERVERS, {
136+
service: 'expo',
137+
token,
138+
});
139+
} catch (err) {
140+
console.warn('Push registration failed:', err);
141+
}
142+
})();
143+
}, [client, currentIdentity?.identityKey]);
144+
129145
useEffect(() => {
130146
let cancelled = false;
131147

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
"run:web": "pnpm -C apps/polycentric run web"
1616
},
1717
"devDependencies": {
18+
"@types/react": "~19.2.10",
1819
"eslint": "^9.38.0",
1920
"prettier": "^3.8.0",
2021
"turbo": "^2.6.1",
2122
"typescript-eslint": "^8.46.2"
23+
},
24+
"dependencies": {
25+
"expo": "^55.0.15"
2226
}
2327
}

packages/js-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"dist"
1616
],
1717
"scripts": {
18-
"generate": "mkdir -p ./src/proto && protoc --ts_out=./src/proto --proto_path=../../protos --experimental_allow_proto3_optional ../../protos/polycentric.proto ../../protos/polycentric/v2/keypair.proto ../../protos/polycentric/v2/event_key.proto ../../protos/polycentric/v2/identity.proto ../../protos/polycentric/v2/content.proto ../../protos/polycentric/v2/events.proto ../../protos/polycentric/v2/feeds.proto ../../protos/polycentric/v2/server.proto ../../protos/polycentric/v2/pairing_service.proto",
18+
"generate": "mkdir -p ./src/proto && protoc --ts_out=./src/proto --proto_path=../../protos --experimental_allow_proto3_optional ../../protos/polycentric.proto ../../protos/polycentric/v2/keypair.proto ../../protos/polycentric/v2/event_key.proto ../../protos/polycentric/v2/identity.proto ../../protos/polycentric/v2/content.proto ../../protos/polycentric/v2/events.proto ../../protos/polycentric/v2/feeds.proto ../../protos/polycentric/v2/server.proto ../../protos/polycentric/v2/pairing_service.proto ../../protos/polycentric/v2/notifications.proto",
1919
"build": "webpack",
2020
"dev": "webpack --watch",
2121
"test": "echo 'No tests'"

packages/js-core/src/grpc/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export {
77
joinPairingSession,
88
listEvents,
99
putEvents,
10+
registerPushNotifications,
1011
setGrpcWebFetch,
1112
uploadBlob,
1213
} from './transport';

packages/js-core/src/grpc/transport.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,3 +148,12 @@ export async function joinPairingSession(
148148
}
149149
return response.session;
150150
}
151+
152+
export async function registerPushNotifications(
153+
serverUrl: string,
154+
signedMessage: Proto.SignedMessage,
155+
): Promise<void> {
156+
await new Proto.NotificationServiceClient(
157+
grpcWebTransport(serverUrl),
158+
).registerPushNotifications(signedMessage).response;
159+
}

packages/js-core/src/platform-interfaces/runtime-core.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,4 +198,16 @@ export interface IPolycentricCore {
198198
* @returns Serialized `GetServerInfoResponse` proto bytes
199199
*/
200200
get_server_info(server_url: string): Promise<Uint8Array>;
201+
202+
/**
203+
* Register a push notification token with a server via gRPC-web.
204+
*
205+
* @param server_url - Base URL of the gRPC-web server
206+
* @param signed_message_bytes - Serialized `SignedMessage` proto bytes
207+
* wrapping a `RegisterPushNotificationRequest`
208+
*/
209+
register_push_notifications(
210+
server_url: string,
211+
signed_message_bytes: Uint8Array,
212+
): Promise<void>;
201213
}

0 commit comments

Comments
 (0)