one 2.7.1 → 2.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,5 +53,16 @@
53
53
  android:configChanges="keyboardHidden|orientation|screenSize"
54
54
  android:exported="false"
55
55
  android:theme="@android:style/Theme.NoTitleBar.Fullscreen" />
56
+ <!-- a scheduled notification's tap records its response here before
57
+ the app opens; react native drops a tap that reaches an app activity
58
+ recreated after process death. never exported: only the notification's
59
+ pending intent launches it. -->
60
+ <activity
61
+ android:name="dev.onejs.onenative.OneNativeNotificationTapActivity"
62
+ android:excludeFromRecents="true"
63
+ android:exported="false"
64
+ android:noHistory="true"
65
+ android:taskAffinity=""
66
+ android:theme="@android:style/Theme.Translucent.NoTitleBar" />
56
67
  </application>
57
68
  </manifest>
@@ -504,9 +504,14 @@ class HybridOneLocation :
504
504
  )
505
505
  return@post
506
506
  }
507
- recentPosition(manager)?.let {
508
- promise.resolve(it)
509
- return@post
507
+ // a live watch keeps the last fix current; without one it can be
508
+ // a fix the device has since moved from, so ask for a fresh one
509
+ // (the ios contract).
510
+ if (monitoring) {
511
+ recentPosition(manager)?.let {
512
+ promise.resolve(it)
513
+ return@post
514
+ }
510
515
  }
511
516
  if (!serviceEnabled(manager)) {
512
517
  promise.reject(
@@ -24,6 +24,7 @@ import com.facebook.react.modules.core.PermissionListener
24
24
  import com.margelo.nitro.NitroModules
25
25
  import com.margelo.nitro.core.AnyMap
26
26
  import com.margelo.nitro.core.Promise
27
+ import dev.onejs.onenative.OneNativeNotificationTapActivity
27
28
  import dev.onejs.onenative.OneNativeNotificationsReceiver
28
29
  import dev.onejs.onenative.OneNativePush
29
30
  import org.json.JSONArray
@@ -601,21 +602,7 @@ class HybridOneNotifications :
601
602
  if (intent == null || !intent.getBooleanExtra(EXTRA_TAP, false)) return
602
603
  // consumed at once: onNewIntent and onHostResume both report a tap.
603
604
  intent.removeExtra(EXTRA_TAP)
604
- val response =
605
- TapResponse(
606
- intent.getStringExtra(EXTRA_IDENTIFIER) ?: return,
607
- intent.getStringExtra(EXTRA_TITLE),
608
- intent.getStringExtra(EXTRA_SUBTITLE),
609
- intent.getStringExtra(EXTRA_BODY),
610
- intent.getStringExtra(EXTRA_DATA) ?: "{}",
611
- intent.getBooleanExtra(EXTRA_SOUND, false),
612
- if (intent.hasExtra(EXTRA_BADGE)) intent.getIntExtra(EXTRA_BADGE, 0) else null,
613
- intent.getStringExtra(EXTRA_TRIGGER) ?: IMMEDIATE_TRIGGER_JSON,
614
- System.currentTimeMillis(),
615
- intent.getStringExtra(EXTRA_ACTION) ?: DEFAULT_ACTION_IDENTIFIER,
616
- intent.getStringExtra(EXTRA_USER_TEXT),
617
- intent.getStringExtra(EXTRA_CATEGORY)
618
- )
605
+ val response = tapResponse(intent, intent.getStringExtra(EXTRA_USER_TEXT)) ?: return
619
606
  lastResponse = response
620
607
  currentListeners()?.onResponse?.invoke(responsePayload(response))
621
608
  }
@@ -626,21 +613,11 @@ class HybridOneNotifications :
626
613
  fun handleAction(context: Context, intent: Intent) {
627
614
  val results = androidx.core.app.RemoteInput.getResultsFromIntent(intent)
628
615
  val response =
629
- TapResponse(
630
- intent.getStringExtra(EXTRA_IDENTIFIER) ?: return,
631
- intent.getStringExtra(EXTRA_TITLE),
632
- intent.getStringExtra(EXTRA_SUBTITLE),
633
- intent.getStringExtra(EXTRA_BODY),
634
- intent.getStringExtra(EXTRA_DATA) ?: "{}",
635
- intent.getBooleanExtra(EXTRA_SOUND, false),
636
- if (intent.hasExtra(EXTRA_BADGE)) intent.getIntExtra(EXTRA_BADGE, 0) else null,
637
- intent.getStringExtra(EXTRA_TRIGGER) ?: IMMEDIATE_TRIGGER_JSON,
638
- System.currentTimeMillis(),
639
- intent.getStringExtra(EXTRA_ACTION) ?: DEFAULT_ACTION_IDENTIFIER,
616
+ tapResponse(
617
+ intent,
640
618
  results?.getCharSequence(EXTRA_USER_TEXT)?.toString()
641
- ?: intent.getStringExtra(EXTRA_USER_TEXT),
642
- intent.getStringExtra(EXTRA_CATEGORY)
643
- )
619
+ ?: intent.getStringExtra(EXTRA_USER_TEXT)
620
+ ) ?: return
644
621
  lastResponse = response
645
622
  currentListeners()?.onResponse?.invoke(responsePayload(response))
646
623
  if (intent.getBooleanExtra(EXTRA_FOREGROUND, true)) {
@@ -708,7 +685,6 @@ class HybridOneNotifications :
708
685
  private const val E_CATEGORY = "E_NOTIFICATIONS_CATEGORY"
709
686
  private const val DEFAULT_CHANNEL_ID = "default"
710
687
  private const val DEFAULT_ACTION_IDENTIFIER = "expo.modules.notifications.actions.DEFAULT"
711
- private const val ACTION_TAP = "dev.onejs.onenative.NOTIFICATION_TAP"
712
688
  private const val EXTRA_TAP = "dev.onejs.onenative.EXTRA_TAP"
713
689
  private const val EXTRA_TITLE = "dev.onejs.onenative.EXTRA_TITLE"
714
690
  private const val EXTRA_SUBTITLE = "dev.onejs.onenative.EXTRA_SUBTITLE"
@@ -1095,15 +1071,9 @@ class HybridOneNotifications :
1095
1071
  if (highPriority) NotificationCompat.PRIORITY_HIGH
1096
1072
  else NotificationCompat.PRIORITY_DEFAULT
1097
1073
  )
1098
- val component = launch?.component
1099
- if (component != null) {
1100
- val target = Intent(ACTION_TAP)
1101
- target.component = component
1102
- target.flags =
1103
- Intent.FLAG_ACTIVITY_NEW_TASK or
1104
- Intent.FLAG_ACTIVITY_CLEAR_TOP or
1105
- Intent.FLAG_ACTIVITY_SINGLE_TOP
1106
- target.putExtra(EXTRA_TAP, true)
1074
+ if (launch?.component != null) {
1075
+ val target = Intent(context, OneNativeNotificationTapActivity::class.java)
1076
+ target.flags = Intent.FLAG_ACTIVITY_NEW_TASK
1107
1077
  target.putExtra(EXTRA_IDENTIFIER, schedule.identifier)
1108
1078
  target.putExtra(EXTRA_TITLE, schedule.title)
1109
1079
  target.putExtra(EXTRA_SUBTITLE, schedule.subtitle)
@@ -1194,6 +1164,46 @@ class HybridOneNotifications :
1194
1164
  return builder.build()
1195
1165
  }
1196
1166
 
1167
+ // a notification tap or action, from the extras every tap intent carries.
1168
+ private fun tapResponse(intent: Intent, userText: String?): TapResponse? {
1169
+ val identifier = intent.getStringExtra(EXTRA_IDENTIFIER) ?: return null
1170
+ return TapResponse(
1171
+ identifier,
1172
+ intent.getStringExtra(EXTRA_TITLE),
1173
+ intent.getStringExtra(EXTRA_SUBTITLE),
1174
+ intent.getStringExtra(EXTRA_BODY),
1175
+ intent.getStringExtra(EXTRA_DATA) ?: "{}",
1176
+ intent.getBooleanExtra(EXTRA_SOUND, false),
1177
+ if (intent.hasExtra(EXTRA_BADGE)) intent.getIntExtra(EXTRA_BADGE, 0) else null,
1178
+ intent.getStringExtra(EXTRA_TRIGGER) ?: IMMEDIATE_TRIGGER_JSON,
1179
+ System.currentTimeMillis(),
1180
+ intent.getStringExtra(EXTRA_ACTION) ?: DEFAULT_ACTION_IDENTIFIER,
1181
+ userText,
1182
+ intent.getStringExtra(EXTRA_CATEGORY)
1183
+ )
1184
+ }
1185
+
1186
+ // a notification body tap, from OneNativeNotificationTapActivity before
1187
+ // the app opens. react native drops an onNewIntent that arrives before
1188
+ // its context is ready, which is how android hands the tap to an app
1189
+ // activity it recreates after the process died, so the response is
1190
+ // recorded on the process here and the app opens without it.
1191
+ fun handleTap(context: Context, intent: Intent) {
1192
+ val response = tapResponse(intent, null) ?: return
1193
+ lastResponse = response
1194
+ instance?.let { live ->
1195
+ live.currentListeners()?.onResponse?.invoke(live.responsePayload(response))
1196
+ }
1197
+ val launch =
1198
+ context.packageManager.getLaunchIntentForPackage(context.packageName) ?: return
1199
+ launch.addFlags(
1200
+ Intent.FLAG_ACTIVITY_NEW_TASK or
1201
+ Intent.FLAG_ACTIVITY_CLEAR_TOP or
1202
+ Intent.FLAG_ACTIVITY_SINGLE_TOP
1203
+ )
1204
+ context.startActivity(launch)
1205
+ }
1206
+
1197
1207
  // action-tap entry: through the live hybrid when js is up, or
1198
1208
  // recorded statically with a response-carrying launch when the
1199
1209
  // process started for this broadcast. the activity harvest reads
@@ -1206,24 +1216,8 @@ class HybridOneNotifications :
1206
1216
  }
1207
1217
  val results = androidx.core.app.RemoteInput.getResultsFromIntent(intent)
1208
1218
  val response =
1209
- TapResponse(
1210
- intent.getStringExtra(EXTRA_IDENTIFIER) ?: return,
1211
- intent.getStringExtra(EXTRA_TITLE),
1212
- intent.getStringExtra(EXTRA_SUBTITLE),
1213
- intent.getStringExtra(EXTRA_BODY),
1214
- intent.getStringExtra(EXTRA_DATA) ?: "{}",
1215
- intent.getBooleanExtra(EXTRA_SOUND, false),
1216
- if (intent.hasExtra(EXTRA_BADGE)) {
1217
- intent.getIntExtra(EXTRA_BADGE, 0)
1218
- } else {
1219
- null
1220
- },
1221
- intent.getStringExtra(EXTRA_TRIGGER) ?: IMMEDIATE_TRIGGER_JSON,
1222
- System.currentTimeMillis(),
1223
- intent.getStringExtra(EXTRA_ACTION) ?: DEFAULT_ACTION_IDENTIFIER,
1224
- results?.getCharSequence(EXTRA_USER_TEXT)?.toString(),
1225
- intent.getStringExtra(EXTRA_CATEGORY)
1226
- )
1219
+ tapResponse(intent, results?.getCharSequence(EXTRA_USER_TEXT)?.toString())
1220
+ ?: return
1227
1221
  lastResponse = response
1228
1222
  if (intent.getBooleanExtra(EXTRA_FOREGROUND, true)) {
1229
1223
  foregroundWithResponse(context, response, results)
@@ -0,0 +1,16 @@
1
+ package dev.onejs.onenative
2
+
3
+ import android.app.Activity
4
+ import android.os.Bundle
5
+ import com.margelo.nitro.one.HybridOneNotifications
6
+
7
+ // a scheduled notification's body tap opens this invisible activity first: it
8
+ // records the response before any app activity sees the tap, then opens the
9
+ // app. never exported, so only the notification's own pending intent reaches it.
10
+ class OneNativeNotificationTapActivity : Activity() {
11
+ override fun onCreate(savedInstanceState: Bundle?) {
12
+ super.onCreate(savedInstanceState)
13
+ HybridOneNotifications.handleTap(this, intent)
14
+ finish()
15
+ }
16
+ }
@@ -8,7 +8,6 @@ import androidx.compose.runtime.LaunchedEffect
8
8
  import androidx.compose.runtime.getValue
9
9
  import androidx.compose.runtime.key
10
10
  import androidx.compose.runtime.mutableStateOf
11
- import androidx.compose.runtime.remember
12
11
  import androidx.compose.runtime.setValue
13
12
  import androidx.compose.runtime.snapshotFlow
14
13
  import androidx.compose.ui.Modifier
@@ -16,7 +15,8 @@ import androidx.compose.ui.graphics.Color
16
15
  import androidx.compose.ui.platform.ComposeView
17
16
  import androidx.compose.ui.platform.ViewCompositionStrategy
18
17
  import androidx.lifecycle.LifecycleOwner
19
- import androidx.lifecycle.ViewTreeLifecycleOwner
18
+ import androidx.lifecycle.findViewTreeLifecycleOwner
19
+ import androidx.lifecycle.setViewTreeLifecycleOwner
20
20
  import com.facebook.react.bridge.ReadableArray
21
21
  import com.facebook.react.uimanager.ThemedReactContext
22
22
  import com.facebook.react.uimanager.UIManagerHelper
@@ -237,10 +237,14 @@ class OneNativeUiMapView(context: Context) : FrameLayout(context) {
237
237
  cameraPositionState.position =
238
238
  CameraPosition.fromLatLngZoom(LatLng(latitude, longitude), zoom.toFloat())
239
239
  }
240
- var gestureMove by remember { mutableStateOf(false) }
241
240
  LaunchedEffect(cameraPositionState) {
241
+ var gestureMove = false
242
242
  snapshotFlow { cameraPositionState.isMoving }.collect { moving ->
243
- if (!moving && gestureMove) {
243
+ if (moving) {
244
+ gestureMove =
245
+ cameraPositionState.cameraMoveStartedReason ==
246
+ CameraMoveStartedReason.GESTURE
247
+ } else if (gestureMove) {
244
248
  gestureMove = false
245
249
  val position = cameraPositionState.position
246
250
  handleCameraMove(
@@ -255,9 +259,6 @@ class OneNativeUiMapView(context: Context) : FrameLayout(context) {
255
259
  modifier = Modifier.fillMaxSize(),
256
260
  cameraPositionState = cameraPositionState,
257
261
  onMapClick = { point -> handleMapClick(point.latitude, point.longitude) },
258
- onCameraMoveStarted = { reason ->
259
- gestureMove = reason == CameraMoveStartedReason.GESTURE
260
- },
261
262
  ) {
262
263
  markers.forEach { marker ->
263
264
  key(marker.id, marker.latitude, marker.longitude, marker.title, marker.tint) {
@@ -314,9 +315,9 @@ class OneNativeUiMapView(context: Context) : FrameLayout(context) {
314
315
  // published here instead. retried on attach: the activity can be null
315
316
  // while the view is constructed.
316
317
  private fun ensureLifecycleOwner() {
317
- if (ViewTreeLifecycleOwner.get(this) != null) return
318
+ if (findViewTreeLifecycleOwner() != null) return
318
319
  val activity = (context as? ThemedReactContext)?.currentActivity as? LifecycleOwner ?: return
319
- ViewTreeLifecycleOwner.set(this, activity)
320
+ setViewTreeLifecycleOwner(activity)
320
321
  }
321
322
 
322
323
  internal fun setLatitude(latitude: Double) {
@@ -12,9 +12,10 @@ swift build --triple arm64-apple-ios17.0-simulator \
12
12
  --package-path packages/one/ios/Tests/ImageManipulator
13
13
  ```
14
14
 
15
- The real `SDWebImageWebPCoder` call shape (`sharedCoder`, `.webP`,
15
+ The real `SDWebImageWebPCoder` call shape (`shared`, `.webP`,
16
16
  `.encodeCompressionQuality`) was read off the vendored 0.15.0 pod headers, not
17
- the stub. The stub only proves the production file is internally consistent.
17
+ the stub; Xcode 27 imports the header's `sharedCoder` class property as
18
+ `shared`. The stub only proves the production file is internally consistent.
18
19
  Real WebP bytes, flip pixels, and validation codes are proven by the
19
20
  native-features `image-manipulator` fixture on the iOS 27 simulator and
20
21
  Android 37 emulator; this harness does not prove transport, sandbox behavior,
@@ -12,7 +12,7 @@ public struct SDImageCoderOption: Hashable {
12
12
  }
13
13
 
14
14
  public final class SDImageWebPCoder {
15
- public static let sharedCoder = SDImageWebPCoder()
15
+ public static let shared = SDImageWebPCoder()
16
16
  public func encodedData(
17
17
  with image: UIImage?,
18
18
  format: SDImageFormat,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "one",
3
- "version": "2.7.1",
3
+ "version": "2.7.3",
4
4
  "description": "One is a React framework for web, iOS, and Android apps.",
5
5
  "license": "BSD-3-Clause",
6
6
  "sideEffects": [
@@ -236,18 +236,18 @@
236
236
  "@react-navigation/native-stack": "8.0.0-alpha.59",
237
237
  "@react-navigation/routers": "8.0.0-alpha.21",
238
238
  "@ungap/structured-clone": "^1.2.0",
239
- "@vxrn/color-scheme": "2.7.1",
240
- "@vxrn/compiler": "2.7.1",
241
- "@vxrn/resolve": "2.7.1",
242
- "@vxrn/tslib-lite": "2.7.1",
243
- "@vxrn/use-isomorphic-layout-effect": "2.7.1",
244
- "@vxrn/utils": "2.7.1",
245
- "@vxrn/vite-plugin-metro": "2.7.1",
239
+ "@vxrn/color-scheme": "2.7.3",
240
+ "@vxrn/compiler": "2.7.3",
241
+ "@vxrn/resolve": "2.7.3",
242
+ "@vxrn/tslib-lite": "2.7.3",
243
+ "@vxrn/use-isomorphic-layout-effect": "2.7.3",
244
+ "@vxrn/utils": "2.7.3",
245
+ "@vxrn/vite-plugin-metro": "2.7.3",
246
246
  "babel-dead-code-elimination": "1.0.10",
247
247
  "babel-plugin-module-resolver": "^5.0.2",
248
248
  "citty": "^0.1.6",
249
249
  "core-js": "^3.38.1",
250
- "create-vxrn": "2.7.1",
250
+ "create-vxrn": "2.7.3",
251
251
  "escape-string-regexp": "^5.0.0",
252
252
  "fast-deep-equal": "^3.1.3",
253
253
  "fast-glob": "^3.3.3",
@@ -269,7 +269,7 @@
269
269
  "tsconfig-paths": "^4",
270
270
  "use-latest-callback": "^0.2.3",
271
271
  "vite": "^8.2.2",
272
- "vxrn": "2.7.1",
272
+ "vxrn": "2.7.3",
273
273
  "web-streams-polyfill": "^4.3.0",
274
274
  "ws": "^8.21.0",
275
275
  "xxhashjs": "^0.2.2"
@@ -325,19 +325,19 @@
325
325
  }
326
326
  },
327
327
  "devDependencies": {
328
- "@tamagui/sheet": "3.0.10",
328
+ "@tamagui/sheet": "3.0.13",
329
329
  "@callstack/liquid-glass": "^0.8.1",
330
330
  "@react-native/babel-plugin-codegen": "0.87.1",
331
331
  "@react-native/codegen": "^0.87.1",
332
332
  "@react-navigation/core": "8.0.0-alpha.41",
333
333
  "@react-navigation/drawer": "8.0.0-alpha.58",
334
334
  "@react-navigation/native": "8.0.0-alpha.51",
335
- "@tamagui/build": "3.0.10",
335
+ "@tamagui/build": "3.0.13",
336
336
  "@types/node": "^24.10.0",
337
337
  "@types/react-dom": "^19.2.2",
338
338
  "@types/react-test-renderer": "^19.1.0",
339
339
  "@types/xxhashjs": "^0.2.4",
340
- "@vxrn/emitter": "2.7.1",
340
+ "@vxrn/emitter": "2.7.3",
341
341
  "depcheck": "^1.4.7",
342
342
  "esbuild": "^0.25.12",
343
343
  "immer": "^10.1.1",
@@ -356,7 +356,7 @@
356
356
  "typescript": "7.0.2",
357
357
  "vitest": "^4.1.0",
358
358
  "yoga-layout": "^3.2.1",
359
- "@tamagui/web": "3.0.10"
359
+ "@tamagui/web": "3.0.13"
360
360
  },
361
361
  "publishConfig": {
362
362
  "access": "public"
@@ -513,7 +513,7 @@
513
513
  }
514
514
  }
515
515
  },
516
- "releaseSourceCommit": "6ef95da2c405283333f006f28f09fff8591f2ae4",
516
+ "releaseSourceCommit": "2f74e66ee7c2641f12fc8d4e1d0169c6a37240c8",
517
517
  "repository": {
518
518
  "type": "git",
519
519
  "url": "git+https://github.com/onejs/one.git",