Skip to content

Commit 0e63e90

Browse files
Merge upstream develop
2 parents 37f0690 + 75ba02a commit 0e63e90

57 files changed

Lines changed: 2252 additions & 627 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitlab/ci/build_services.yml

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@
1515
- cargo clippy --package ${SERVICE_NAME} -- -D warnings
1616
allow_failure: true
1717

18+
.service-test:
19+
extends: .service-rust-base
20+
script:
21+
- cargo test --package ${SERVICE_NAME}
22+
1823
.service-build:
1924
extends: .service-rust-base
2025
stage: build
@@ -151,4 +156,62 @@ moderation-image:
151156
- .service-image
152157
rules:
153158
- !reference [.moderation-workflow, rules]
154-
- !reference [.release-workflow, rules]
159+
- !reference [.release-workflow, rules]
160+
161+
########################################################################
162+
# notifications
163+
########################################################################
164+
165+
.notifications-workflow:
166+
image: gitlab.futo.org:5050/devops/manifest-repo/library/docker:latest
167+
rules:
168+
- if: $CI_COMMIT_TAG =~ /^notifications-/
169+
when: always
170+
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
171+
changes:
172+
- Cargo.toml
173+
- Cargo.lock
174+
- services/notifications/**/*
175+
- services/common/**/*
176+
- .gitlab/ci/build_services.yml
177+
- .gitlab/ci/deploy_services.yml
178+
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
179+
when: never
180+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
181+
changes:
182+
- services/notifications/**/*
183+
- services/common/**/*
184+
- .gitlab/ci/build_services.yml
185+
- .gitlab/ci/deploy_services.yml
186+
variables:
187+
RUNNER_SCRIPT_TIMEOUT: 179m
188+
SERVICE_NAME: notifications-service
189+
SERVICE_DIR: notifications
190+
191+
notifications-format-check:
192+
extends:
193+
- .notifications-workflow
194+
- .service-format-check
195+
196+
notifications-clippy:
197+
extends:
198+
- .notifications-workflow
199+
- .service-clippy
200+
201+
notifications-test:
202+
extends:
203+
- .notifications-workflow
204+
- .service-test
205+
206+
notifications-build:
207+
extends:
208+
- .notifications-workflow
209+
- .service-build
210+
211+
notifications-image:
212+
extends:
213+
- .notifications-workflow
214+
- .service-image
215+
rules:
216+
- !reference [.notifications-workflow, rules]
217+
- !reference [.release-workflow, rules]

.gitlab/ci/deploy_pages.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
fusmi-2026-web:
22
stage: deploy
3-
image: node:20
3+
image: node:24
44
rules:
55
- if: $CI_COMMIT_TAG == "fusmi-2026-web"
66
before_script:

.gitlab/ci/deploy_services.yml

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,47 @@ moderation-deploy-manifest-production:
118118
variables:
119119
MANIFEST_QUICK_TARGETS: inventory/polycentric-east/moderation.yml:polycentriceast
120120
needs:
121-
- moderation-deploy-image-production
121+
- moderation-deploy-image-production
122+
123+
########################################################################
124+
# notifications
125+
########################################################################
126+
127+
.notifications-deploy-workflow:
128+
rules:
129+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
130+
changes:
131+
- services/notifications/**/*
132+
- services/common/**/*
133+
- .gitlab/ci/build_services.yml
134+
- .gitlab/ci/deploy_services.yml
135+
variables:
136+
SERVICE_NAME: notifications-service
137+
138+
########################################
139+
# Tag the image with environment label #
140+
########################################
141+
142+
notifications-deploy-image-production:
143+
extends:
144+
- .notifications-deploy-workflow
145+
- .service-deploy-image
146+
when: manual
147+
variables:
148+
ENVIRONMENT: production
149+
needs:
150+
- job: notifications-image
151+
artifacts: false
152+
153+
###############################
154+
# Rolling restart on Manifest #
155+
###############################
156+
157+
notifications-deploy-manifest-production:
158+
extends:
159+
- .notifications-deploy-workflow
160+
- .service-deploy-manifest
161+
variables:
162+
MANIFEST_QUICK_TARGETS: inventory/polycentric-east/notifications.yml:polycentriceast
163+
needs:
164+
- notifications-deploy-image-production

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v24.14.0
1+
24.16.0

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"services/server",
66
"services/server/tests",
77
"services/moderation",
8+
"services/notifications",
89
"services/common/*",
910
"tools/identity",
1011
]

apps/polycentric/.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
# ServerService.GetInfo, so only the gRPC endpoint(s) need to be set.
66
EXPO_PUBLIC_POLYCENTRIC_SEED_SERVERS=http://localhost:3000
77

8+
# Comma-separated gRPC-web URLs of the notifications service the client
9+
# registers Expo push tokens with. Falls back to `http://<host>:3001`.
10+
EXPO_PUBLIC_POLYCENTRIC_NOTIFICATION_SERVERS=http://localhost:3001
11+
812
# Host Metro and the app resolve `localhost` against. Set to your dev
913
# machine's LAN IP when running on a physical device/emulator.
1014
REACT_NATIVE_PACKAGER_HOSTNAME=localhost
15+
16+
# Path to the Firebase `google-services.json` used for Android push
17+
# notifications. Defaults to `./google-services.json` (this directory)
18+
# when unset; If no google services file is present, Android push
19+
# notifications will not work.
20+
GOOGLE_SERVICES_JSON=./google-services.json

apps/polycentric/app.config.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import type { ExpoConfig, ConfigContext } from 'expo/config';
1+
import type { ConfigContext, ExpoConfig } from 'expo/config';
2+
import fs from 'fs';
23

34
const { version: PKG_VERSION } = require('./package.json');
45

@@ -7,6 +8,10 @@ const IS_DEV = process.env.APP_VARIANT === 'dev';
78
const NAME = IS_DEV ? 'Polycentric Dev' : 'Polycentric';
89
const ID = IS_DEV ? 'org.futo.polycentric.dev' : 'org.futo.polycentric';
910

11+
const GOOGLE_SERVICES_FILE =
12+
process.env.GOOGLE_SERVICES_JSON ?? './google-services.json';
13+
const HAS_GOOGLE_SERVICES = fs.existsSync(GOOGLE_SERVICES_FILE);
14+
1015
export default ({ config }: ConfigContext): ExpoConfig => ({
1116
...config,
1217
name: NAME,
@@ -32,9 +37,6 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
3237
NSCameraUsageDescription: '$(PRODUCT_NAME) needs access to your Camera.',
3338
ITSAppUsesNonExemptEncryption: false,
3439
},
35-
entitlements: {
36-
'aps-environment': 'production',
37-
},
3840
},
3941
android: {
4042
adaptiveIcon: {
@@ -48,6 +50,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
4850
},
4951
package: ID,
5052
permissions: ['android.permission.CAMERA'],
53+
...(HAS_GOOGLE_SERVICES && { googleServicesFile: GOOGLE_SERVICES_FILE }),
5154
},
5255
plugins: [
5356
'expo-router',

apps/polycentric/src/common/components/layout/Layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ export const RightSidebar = memo(function RightSidebar({
352352
style={({ pressed }) => [pressed && { opacity: 0.65 }]}
353353
>
354354
<Icon
355-
name={theme.name === 'dark' ? 'themeDark' : 'themeLight'}
355+
name={theme.name === 'dark' ? 'themeLight' : 'themeDark'}
356356
size={typography.fontSize.sm}
357357
color="neutral_500"
358358
/>

apps/polycentric/src/common/lib/notifications/registerPushToken.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,22 @@ import * as Device from 'expo-device';
33
import * as Notifications from 'expo-notifications';
44
import { Platform } from 'react-native';
55

6+
// Without a handler, expo-notifications silently delivers pushes while the
7+
// app is foregrounded — the OS suppresses the banner/sound. Setting this
8+
// at module load (rather than inside a component) ensures it's registered
9+
// before the first notification arrives.
10+
Notifications.setNotificationHandler({
11+
handleNotification: async () => ({
12+
shouldShowBanner: true,
13+
shouldShowList: true,
14+
shouldPlaySound: true,
15+
shouldSetBadge: false,
16+
}),
17+
});
18+
619
export async function registerForPushNotifications(): Promise<string | null> {
7-
if (Platform.OS === 'web' || !Device.isDevice) return null;
20+
if (Platform.OS === 'web') return null;
21+
if (!Device.isDevice && !__DEV__) return null;
822

923
if (Platform.OS === 'android') {
1024
await Notifications.setNotificationChannelAsync('default', {

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,24 @@ export const DEFAULT_SEED_SERVERS: string[] = (() => {
5959
/** First seed server — used by identity onboarding helpers. */
6060
export const DEFAULT_SERVER = DEFAULT_SEED_SERVERS[0]!;
6161

62+
/**
63+
* Comma-separated list of gRPC-web URLs for the notification service the
64+
* client registers push tokens with. Read from
65+
* `EXPO_PUBLIC_POLYCENTRIC_NOTIFICATION_SERVERS`; falls back to
66+
* `http://<host>:3001` for local dev (the notifications service's default
67+
* gRPC port).
68+
*/
69+
export const DEFAULT_NOTIFICATION_SERVERS: string[] = (() => {
70+
const raw = (
71+
process.env.EXPO_PUBLIC_POLYCENTRIC_NOTIFICATION_SERVERS ?? ''
72+
).trim();
73+
const parsed = raw
74+
.split(',')
75+
.map((s) => s.trim())
76+
.filter((s) => s.length > 0);
77+
return parsed.length > 0 ? parsed : [`http://${DEFAULT_HOST}:3001`];
78+
})();
79+
6280
interface PolycentricProviderProps {
6381
children: ReactNode;
6482
loadingComponent?: ReactNode;
@@ -132,7 +150,7 @@ export function PolycentricProvider({
132150
try {
133151
const token = await registerForPushNotifications();
134152
if (!token) return;
135-
await client.registerPushNotifications(DEFAULT_SEED_SERVERS, {
153+
await client.registerPushNotifications(DEFAULT_NOTIFICATION_SERVERS, {
136154
service: 'expo',
137155
token,
138156
});

0 commit comments

Comments
 (0)