one 2.7.3 → 2.7.5

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.
Files changed (31) hide show
  1. package/android/src/main/java/com/margelo/nitro/one/HybridOneFonts.kt +23 -8
  2. package/android/src/main/java/com/margelo/nitro/one/HybridOneNotifications.kt +5 -1
  3. package/android/src/main/java/com/margelo/nitro/one/HybridOneSpeechSynthesis.kt +8 -4
  4. package/android/src/main/java/dev/onejs/onenative/OneNativeComposeNodeView.kt +3 -2
  5. package/android/src/main/java/dev/onejs/onenative/OneNativePagerManager.kt +2 -0
  6. package/dist/cjs/platform/compose.android.cjs +1 -1
  7. package/dist/cjs/platform/compose.android.js +1 -1
  8. package/dist/cjs/platform/compose.android.js.map +1 -1
  9. package/dist/cjs/platform/generated/Controls.native.cjs +6 -6
  10. package/dist/cjs/platform/generated/Controls.native.js +6 -6
  11. package/dist/cjs/platform/generated/Controls.native.js.map +1 -1
  12. package/dist/cjs/vite/resolveResponse.cjs +15 -2
  13. package/dist/cjs/vite/resolveResponse.native.cjs +17 -2
  14. package/dist/cjs/vite/resolveResponse.native.js +17 -2
  15. package/dist/cjs/vite/resolveResponse.native.js.map +1 -1
  16. package/dist/esm/platform/compose.android.js +1 -1
  17. package/dist/esm/platform/compose.android.js.map +1 -1
  18. package/dist/esm/platform/generated/Controls.native.js +6 -6
  19. package/dist/esm/platform/generated/Controls.native.js.map +1 -1
  20. package/dist/esm/vite/resolveResponse.mjs +15 -2
  21. package/dist/esm/vite/resolveResponse.mjs.map +1 -1
  22. package/dist/esm/vite/resolveResponse.native.js +17 -2
  23. package/dist/esm/vite/resolveResponse.native.js.map +1 -1
  24. package/ios/ZoomTransition/ZoomTransition.swift +52 -15
  25. package/package.json +15 -15
  26. package/src/createHandleRequest.test.ts +98 -0
  27. package/src/platform/compose.android.tsx +5 -1
  28. package/src/platform/generated/Controls.native.tsx +6 -6
  29. package/src/vite/resolveResponse.ts +17 -2
  30. package/types/platform/compose.android.d.ts.map +1 -1
  31. package/types/vite/resolveResponse.d.ts.map +1 -1
@@ -126,11 +126,9 @@ class HybridOneFonts : HybridOneFontsSpec() {
126
126
  }
127
127
  // the resource name carries no extension; createFromFile sniffs
128
128
  // the content, so the suffix is only a label.
129
- val directory = File(context.cacheDir, "one-fonts")
130
- val file = File(directory, "$resourceName.ttf")
129
+ val file = File(File(context.cacheDir, "one-fonts"), "$resourceName.ttf")
131
130
  if (!file.isFile) {
132
- directory.mkdirs()
133
- file.writeBytes(bytes)
131
+ writeAtomically(file, bytes)
134
132
  }
135
133
  return file
136
134
  }
@@ -139,8 +137,7 @@ class HybridOneFonts : HybridOneFontsSpec() {
139
137
  val extension = url.path.substringAfterLast('.', "ttf").lowercase()
140
138
  val digest = MessageDigest.getInstance("SHA-256").digest(url.toString().toByteArray())
141
139
  val fileName = digest.joinToString("") { "%02x".format(it) } + "." + extension
142
- val directory = File(context.cacheDir, "one-fonts")
143
- val file = File(directory, fileName)
140
+ val file = File(File(context.cacheDir, "one-fonts"), fileName)
144
141
  if (file.isFile) {
145
142
  return file
146
143
  }
@@ -148,8 +145,26 @@ class HybridOneFonts : HybridOneFontsSpec() {
148
145
  if (bytes.isEmpty()) {
149
146
  throw OneNativeError("E_FONTS_DOWNLOAD", "Fonts.load: \"$name\" downloaded zero bytes")
150
147
  }
151
- directory.mkdirs()
152
- file.writeBytes(bytes)
148
+ writeAtomically(file, bytes)
153
149
  return file
154
150
  }
151
+
152
+ // concurrent loads of one font (a StrictMode double mount, or two
153
+ // screens) each write the cache file. writing in place truncates a file
154
+ // another load has already mapped for Typeface, which faults with
155
+ // SIGBUS, so each write goes to its own temp file and renames over the
156
+ // target, like the ios atomic write.
157
+ private fun writeAtomically(file: File, bytes: ByteArray) {
158
+ val directory = file.parentFile ?: throw IllegalStateException("Fonts: no cache directory")
159
+ directory.mkdirs()
160
+ val temp = File.createTempFile(file.name, ".part", directory)
161
+ try {
162
+ temp.writeBytes(bytes)
163
+ if (!temp.renameTo(file)) {
164
+ throw IllegalStateException("Fonts: could not move ${temp.name} into place")
165
+ }
166
+ } finally {
167
+ temp.delete()
168
+ }
169
+ }
155
170
  }
@@ -1147,7 +1147,11 @@ class HybridOneNotifications :
1147
1147
  context,
1148
1148
  (schedule.identifier + ":" + action.identifier).hashCode(),
1149
1149
  target,
1150
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
1150
+ // the system writes a typed reply into the intent, so a
1151
+ // text-input action needs a mutable one (refused otherwise).
1152
+ PendingIntent.FLAG_UPDATE_CURRENT or
1153
+ if (action.textInputPlaceholder != null) PendingIntent.FLAG_MUTABLE
1154
+ else PendingIntent.FLAG_IMMUTABLE
1151
1155
  )
1152
1156
  val builder =
1153
1157
  androidx.core.app.NotificationCompat.Action.Builder(0, action.buttonTitle, pending)
@@ -190,10 +190,11 @@ class HybridOneSpeechSynthesis : HybridOneSpeechSynthesisSpec() {
190
190
  override fun stop(): Promise<Unit> {
191
191
  val promise = Promise<Unit>()
192
192
  main.post {
193
- // queued utterances that never started get no engine callback, so
194
- // they settle here; the running one settles in onStop above.
195
- val dropped = waiting.toList()
193
+ // every utterance settles here, so isSpeaking is false once stop
194
+ // resolves; the engine's later onStop finds nothing live.
195
+ val dropped = live.values.toList() + waiting
196
196
  waiting.clear()
197
+ live.clear()
197
198
  engine?.stop()
198
199
  dropped.forEach {
199
200
  it.onEvent(SpeechSynthesisEvent(SpeechSynthesisEventType.STOPPED, null))
@@ -209,9 +210,12 @@ class HybridOneSpeechSynthesis : HybridOneSpeechSynthesisSpec() {
209
210
 
210
211
  override fun resume(): Promise<Unit> = Promise.resolved(Unit)
211
212
 
213
+ // speaking or queued on this synthesizer, as AVSpeechSynthesizer reports
214
+ // on ios. the engine's own isSpeaking spans every app and stays true
215
+ // until its playback thread finishes the item that already sent onDone.
212
216
  override fun isSpeaking(): Promise<Boolean> {
213
217
  val promise = Promise<Boolean>()
214
- main.post { promise.resolve(engine?.isSpeaking == true) }
218
+ main.post { promise.resolve(waiting.isNotEmpty() || live.isNotEmpty()) }
215
219
  return promise
216
220
  }
217
221
 
@@ -9,7 +9,6 @@ import android.graphics.PorterDuffColorFilter
9
9
  import android.graphics.drawable.Animatable
10
10
  import android.net.Uri
11
11
  import android.os.Build
12
- import android.view.View
13
12
  import android.view.ViewGroup
14
13
  import androidx.compose.foundation.BorderStroke
15
14
  import androidx.compose.foundation.background
@@ -995,8 +994,10 @@ private class OneNativeControlledValue<T>(initial: T) {
995
994
 
996
995
  class OneNativeComposeNodeView(context: Context) : ReactViewGroup(context) {
997
996
  private val composeView =
997
+ // no view id: react native takes the nearest ancestor with a positive
998
+ // id as the react tag of a touch, so a generated id here sent taps on
999
+ // compose controls to whatever react view shared that number.
998
1000
  ComposeView(context).apply {
999
- id = View.generateViewId()
1000
1001
  layoutParams =
1001
1002
  ViewGroup.LayoutParams(
1002
1003
  ViewGroup.LayoutParams.MATCH_PARENT,
@@ -66,6 +66,8 @@ class OneNativePagerView(val reactContext: ThemedReactContext) : FrameLayout(rea
66
66
  layoutParams = ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
67
67
  isSaveEnabled = false
68
68
  pager.isSaveEnabled = false
69
+ // the internal recycler is not a react view; a positive id becomes a touch target tag.
70
+ pager.getChildAt(0).id = View.NO_ID
69
71
  pager.adapter = adapter
70
72
  addView(pager, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
71
73
  pager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
@@ -1234,7 +1234,7 @@ function TextField(param) {
1234
1234
  var syncHandle = (0, import_syncNativeState.syncHandleOf)(text);
1235
1235
  var syncedText = (0, import_syncNativeState.useSyncValue)(text);
1236
1236
  var controlled = (0, import_controlled.useControlled)(function(event) {
1237
- syncHandle === null || syncHandle === void 0 ? void 0 : syncHandle.set(event.text);
1237
+ syncHandle === null || syncHandle === void 0 ? void 0 : syncHandle.set(syncHandle.get());
1238
1238
  onTextChange(event.text);
1239
1239
  }, revision);
1240
1240
  var controlledFocus = (0, import_controlled.useControlled)(function(event) {
@@ -1234,7 +1234,7 @@ function TextField(param) {
1234
1234
  var syncHandle = (0, import_syncNativeState.syncHandleOf)(text);
1235
1235
  var syncedText = (0, import_syncNativeState.useSyncValue)(text);
1236
1236
  var controlled = (0, import_controlled.useControlled)(function(event) {
1237
- syncHandle === null || syncHandle === void 0 ? void 0 : syncHandle.set(event.text);
1237
+ syncHandle === null || syncHandle === void 0 ? void 0 : syncHandle.set(syncHandle.get());
1238
1238
  onTextChange(event.text);
1239
1239
  }, revision);
1240
1240
  var controlledFocus = (0, import_controlled.useControlled)(function(event) {