@onekeyfe/react-native-native-list 3.0.114 → 3.0.116

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.
package/README.md CHANGED
@@ -112,9 +112,10 @@ second delta.
112
112
  | `activity` | Transaction/activity with amounts and up to three actions |
113
113
  | `message` | Notification/message with bounded body lines and thumbnail |
114
114
  | `dataRow` | Two to four fixed table columns, optional index/checkbox |
115
+ | `market` | Recycled token, stock, or perpetual quote row |
115
116
  | `mediaTile` | Gallery or browser-preview tile |
116
- | `metricCard` | KPI or fixed Activity/Performance portfolio card |
117
- | `sectionHeader` | Sticky/group, tri-state, or fixed list-summary header |
117
+ | `metricCard` | KPI or fixed Activity/Performance portfolio card |
118
+ | `sectionHeader` | Sticky/group, tri-state, or fixed list-summary header |
118
119
  | `action` | Fixed action row or fixed footer |
119
120
  | `system` | Loading, retry, end marker, or bounded spacer |
120
121
 
@@ -162,6 +163,59 @@ Bold faces and use them before the platform-font fallback. Their semantic
162
163
  icons and checkbox marks are the same vector paths as the OneKey component
163
164
  library, rendered by Core Graphics/UIKit on iOS and Canvas on Android.
164
165
 
166
+ ### Market rows
167
+
168
+ `market` is a fixed native/DOM template, not a React `renderItem`. Its default
169
+ token/perpetual geometry uses a 32-point leading image with a 16-point network
170
+ corner; stock uses a 40-point image. The trailing change block is 80×32 with an
171
+ 8-point radius. All displayed values are already formatted strings; compact
172
+ zero runs can use `priceSegments`, `subtitleSegments`, or
173
+ `change.textSegments` with `{ style: 'subscript' }`.
174
+
175
+ ```ts
176
+ const btc = {
177
+ type: 'market',
178
+ key: 'btc',
179
+ variant: 'token',
180
+ leading: { kind: 'token', image: btcImage, networkImage: bitcoinImage },
181
+ title: 'BTC',
182
+ subtitle: '$1.23B',
183
+ price: '$64,230.00',
184
+ change: { text: '+2.40%', tone: 'positive' },
185
+ badges: [
186
+ {
187
+ key: 'community',
188
+ iconName: 'verified',
189
+ tone: 'success',
190
+ actionKey: 'market.communityInfo',
191
+ accessibilityLabel: 'Community recognized',
192
+ },
193
+ ],
194
+ pressInActionKey: 'market.prewarm',
195
+ pressActionKey: 'market.open',
196
+ longPressActionKey: 'market.menu',
197
+ diagnostics: { imageBindActionKey: 'market.imageBind' },
198
+ } satisfies MarketRow;
199
+ ```
200
+
201
+ Badges accept localized text, a bounded remote image, or the single built-in
202
+ `iconName: 'verified'` glyph. A badge `actionKey` emits independently with an
203
+ anchor whose source is `marketBadge`. Market long press fires after 800 ms and
204
+ is cancelled when the pointer moves more than 10 logical pixels or the list
205
+ scrolls. The optional diagnostic action fires when a row binds an image; quote-
206
+ only patches (`price`, `priceSegments`, `change`, `accessibilityLabel`, and
207
+ `revision`) update visible labels without rebinding that image.
208
+
209
+ `style` is deliberately finite: row padding/gaps, image width/height/shape/
210
+ corner radius/content fit, title/subtitle/price/change font size, weight,
211
+ color, line height, one-or-two-line truncation and alignment, plus change-block
212
+ width/height/radius. Omitting it preserves the defaults. Loading, empty, retry,
213
+ and pagination states use the existing `system` rows with
214
+ `presentation: 'market'`. Loading rows accept `loadingStyle: 'skeleton'` for
215
+ the 56-point token skeleton or `'spinner'` for the centered 52-point pagination
216
+ indicator. Use `accessibilityLabel` for loading announcements. Omitting
217
+ `loadingStyle` preserves each platform's existing loading rendering.
218
+
165
219
  For native reorder, set `capabilities.reorderable` and mark eligible rail rows
166
220
  with `draggable: true`, or add a `drag` trailing accessory to an identity row.
167
221
  Reordering never crosses a `sectionKey` boundary.
@@ -307,7 +361,7 @@ by the iOS wallet list's 120 Hz tail behavior.
307
361
  | Activity rows | `components/TxHistoryListView/TxHistoryListItem.tsx` |
308
362
  | Message rows | `Notifications/components/NotificationListView.tsx` |
309
363
  | Data rows | `Perp/components/TokenSelector/PerpTokenSelectorRow.tsx` |
310
- | Media tile rows | `Home/components/NFTListView/NFTListItem.tsx` |
364
+ | Media tile rows | `Home/components/NFTListView/NFTListItem.tsx` |
311
365
  | Metric card rows | `Perp/components/Portfolio/PerpPortfolioContent.tsx` |
312
366
  | Section header rows | `AllNetworksManager/NetworksSectionList.tsx`, `TxHistoryListView/TxHistorySectionHeader.tsx` |
313
367
  | Action rows | `TokenManager/TokenManagerList.tsx`, `Discovery/pages/BookmarkListModal/index.tsx` |
@@ -101,5 +101,6 @@ dependencies {
101
101
  implementation "androidx.recyclerview:recyclerview:1.4.0"
102
102
  implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
103
103
  implementation project(":react-native-nitro-modules")
104
+ implementation project(":onekeyfe_react-native-native-logger")
104
105
  implementation project(":onekeyfe_react-native-image")
105
106
  }
@@ -10,6 +10,8 @@ import com.facebook.react.uimanager.ThemedReactContext
10
10
  import org.json.JSONObject
11
11
  import java.util.Collections
12
12
  import java.util.WeakHashMap
13
+ import kotlin.math.ceil
14
+ import kotlin.math.roundToInt
13
15
 
14
16
  internal class NativeListAdapter(
15
17
  private val context: ThemedReactContext,
@@ -39,6 +41,8 @@ internal class NativeListAdapter(
39
41
  )
40
42
  val currentList: List<NativeListItem>
41
43
  get() = differ.currentList
44
+ private var marketSourceItems: List<NativeListItem>? = null
45
+ private var marketSourceEdges = DoubleArray(0)
42
46
  private val createdRows = Collections.newSetFromMap(
43
47
  WeakHashMap<NativeListRowView, Boolean>(),
44
48
  )
@@ -92,6 +96,10 @@ internal class NativeListAdapter(
92
96
  position: Int,
93
97
  payloads: MutableList<Any>,
94
98
  ) {
99
+ if (payloads.isNotEmpty() && payloads.all { it == MARKET_QUOTE_PAYLOAD }) {
100
+ itemAt(position)?.let { holder.rowView.bindMarketQuote(it, theme) }
101
+ return
102
+ }
95
103
  // OneKey patch: an unknown payload must retain full binding; validated echoes keep images alive.
96
104
  // if (payloads.contains(SELECTION_PAYLOAD)) {
97
105
  if (payloads.isNotEmpty() && payloads.all { it == SELECTION_PAYLOAD || it == SELECTION_ECHO_PAYLOAD }) {
@@ -118,6 +126,30 @@ internal class NativeListAdapter(
118
126
  fun itemAt(position: Int): NativeListItem? =
119
127
  reorderItems?.getOrNull(position) ?: differ.currentList.getOrNull(position)
120
128
 
129
+ // OneKey patch: Yoga rounds badge dimensions from unrounded cumulative row edges.
130
+ fun marketSourceEdgePx(position: Int): Double {
131
+ val items = reorderItems ?: differ.currentList
132
+ if (marketSourceItems !== items) {
133
+ val density = context.resources.displayMetrics.density.toDouble()
134
+ val edges = DoubleArray(items.size + 1)
135
+ items.forEachIndexed { index, item ->
136
+ val style = item.json.optJSONObject("style")
137
+ val height = item.json.optDouble("height", 0.0)
138
+ val sourceHeight = if (item.type == "market" && style != null) {
139
+ val titleHeight = ceil((style.optJSONObject("title")?.optDouble("lineHeight", 24.0) ?: 24.0) * density) / density
140
+ val subtitleHeight = if (item.json.optString("subtitle").isNotEmpty() || item.json.optJSONObject("subtitlePrefix") != null) {
141
+ ceil((style.optJSONObject("subtitle")?.optDouble("lineHeight", 20.0) ?: 20.0) * density) / density + style.optDouble("lineGap", 0.0)
142
+ } else 0.0
143
+ maxOf(height.roundToInt().toDouble(), titleHeight + subtitleHeight + style.optDouble("verticalPadding", 12.0) * 2).toFloat().toDouble()
144
+ } else height
145
+ edges[index + 1] = edges[index] + sourceHeight * density
146
+ }
147
+ marketSourceItems = items
148
+ marketSourceEdges = edges
149
+ }
150
+ return marketSourceEdges.getOrElse(position) { 0.0 }
151
+ }
152
+
121
153
  fun positionOfKey(key: String): Int =
122
154
  (reorderItems ?: differ.currentList).indexOfFirst { it.key == key }
123
155
 
@@ -165,6 +197,8 @@ internal class NativeListAdapter(
165
197
  }
166
198
 
167
199
  fun dispose() {
200
+ marketSourceItems = null
201
+ marketSourceEdges = DoubleArray(0)
168
202
  reorderItems = null
169
203
  suppressDifferUpdates = false
170
204
  createdRows.forEach(NativeListRowView::dispose)
@@ -183,7 +217,18 @@ internal class NativeListAdapter(
183
217
  override fun getChangePayload(oldItem: NativeListItem, newItem: NativeListItem): Any? =
184
218
  if (oldItem.key == newItem.key && oldItem.type == newItem.type &&
185
219
  newItem.selectionUpdateFromContent == oldItem.content
186
- ) SELECTION_ECHO_PAYLOAD else null
220
+ ) SELECTION_ECHO_PAYLOAD else if (isMarketQuoteUpdate(oldItem, newItem)) MARKET_QUOTE_PAYLOAD else null
221
+
222
+ private fun isMarketQuoteUpdate(oldItem: NativeListItem, newItem: NativeListItem): Boolean {
223
+ if (oldItem.type != "market" || newItem.type != "market") return false
224
+ fun stableContent(item: NativeListItem): String {
225
+ val value = JSONObject(item.json.toString())
226
+ listOf("revision", "price", "priceSegments", "change", "accessibilityLabel")
227
+ .forEach(value::remove)
228
+ return value.toString()
229
+ }
230
+ return stableContent(oldItem) == stableContent(newItem)
231
+ }
187
232
  }
188
233
  }
189
234
  }
@@ -191,3 +236,4 @@ internal class NativeListAdapter(
191
236
  internal const val SELECTION_PAYLOAD = "selection"
192
237
  // OneKey patch: internal payload, never exposed through the serialized row contract.
193
238
  internal const val SELECTION_ECHO_PAYLOAD = "selectionEcho"
239
+ internal const val MARKET_QUOTE_PAYLOAD = "marketQuote"