@onekeyfe/react-native-native-list 3.0.157-alpha.249 → 3.0.157-alpha.251

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 (130) hide show
  1. package/README.md +21 -5
  2. package/android/src/main/java/com/onekey/nativelist/NativeListAccessoryStack.kt +711 -0
  3. package/android/src/main/java/com/onekey/nativelist/NativeListActionRenderer.kt +169 -0
  4. package/android/src/main/java/com/onekey/nativelist/NativeListActivityRenderer.kt +243 -0
  5. package/android/src/main/java/com/onekey/nativelist/NativeListAdapter.kt +20 -6
  6. package/android/src/main/java/com/onekey/nativelist/NativeListDataRowRenderer.kt +487 -0
  7. package/android/src/main/java/com/onekey/nativelist/NativeListIdentityRenderer.kt +820 -0
  8. package/android/src/main/java/com/onekey/nativelist/NativeListImageSlot.kt +147 -0
  9. package/android/src/main/java/com/onekey/nativelist/NativeListLeadingVisual.kt +453 -0
  10. package/android/src/main/java/com/onekey/nativelist/NativeListMarketRenderer.kt +997 -0
  11. package/android/src/main/java/com/onekey/nativelist/NativeListMediaTileRenderer.kt +234 -0
  12. package/android/src/main/java/com/onekey/nativelist/NativeListMessageRenderer.kt +232 -0
  13. package/android/src/main/java/com/onekey/nativelist/NativeListMetricCardRenderer.kt +551 -0
  14. package/android/src/main/java/com/onekey/nativelist/NativeListModels.kt +24 -0
  15. package/android/src/main/java/com/onekey/nativelist/NativeListPrimitives.kt +475 -0
  16. package/android/src/main/java/com/onekey/nativelist/NativeListRailRenderer.kt +143 -0
  17. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRegistry.kt +42 -0
  18. package/android/src/main/java/com/onekey/nativelist/NativeListRendererRowView.kt +415 -0
  19. package/android/src/main/java/com/onekey/nativelist/NativeListResolvedText.kt +163 -0
  20. package/android/src/main/java/com/onekey/nativelist/NativeListRowHost.kt +70 -0
  21. package/android/src/main/java/com/onekey/nativelist/NativeListSectionHeaderRenderer.kt +748 -0
  22. package/android/src/main/java/com/onekey/nativelist/NativeListSystemRenderer.kt +464 -0
  23. package/android/src/main/java/com/onekey/nativelist/NativeListTextView.kt +25 -0
  24. package/android/src/main/java/com/onekey/nativelist/NativeListView.kt +174 -104
  25. package/android/src/main/java/com/onekey/nativelist/NativeListWalletGroupRenderer.kt +407 -0
  26. package/android/src/test/java/com/margelo/nitro/nativelist/NativeListSelectorBackgroundColorTest.kt +34 -0
  27. package/docs/DESIGN.md +518 -9
  28. package/docs/MIGRATION_BASELINE.md +117 -0
  29. package/docs/ROW_TEMPLATES.md +359 -0
  30. package/docs/SPEC.md +358 -0
  31. package/docs/STYLE_SPEC.md +978 -0
  32. package/docs/images/list-capabilities.svg +39 -0
  33. package/docs/images/row-action.svg +17 -0
  34. package/docs/images/row-activity.svg +24 -0
  35. package/docs/images/row-data.svg +24 -0
  36. package/docs/images/row-identity.svg +25 -0
  37. package/docs/images/row-market.svg +23 -0
  38. package/docs/images/row-media-tile.svg +26 -0
  39. package/docs/images/row-message.svg +20 -0
  40. package/docs/images/row-metric-card.svg +33 -0
  41. package/docs/images/row-rail.svg +18 -0
  42. package/docs/images/row-section-header.svg +19 -0
  43. package/docs/images/row-style-regions.svg +30 -0
  44. package/docs/images/row-system.svg +30 -0
  45. package/docs/images/row-wallet-group.svg +26 -0
  46. package/ios/NativeListAccessoryStack.swift +680 -0
  47. package/ios/NativeListActionRenderer.swift +108 -0
  48. package/ios/NativeListActivityRenderer.swift +179 -0
  49. package/ios/NativeListDataRowRenderer.swift +444 -0
  50. package/ios/NativeListIdentityRenderer.swift +685 -0
  51. package/ios/NativeListImageSlot.swift +140 -0
  52. package/ios/NativeListLeadingVisual.swift +372 -0
  53. package/ios/NativeListMarketRenderer.swift +698 -0
  54. package/ios/NativeListMediaTileRenderer.swift +184 -0
  55. package/ios/NativeListMessageRenderer.swift +168 -0
  56. package/ios/NativeListMetricCardRenderer.swift +420 -0
  57. package/ios/NativeListModels.swift +32 -0
  58. package/ios/NativeListRailRenderer.swift +152 -0
  59. package/ios/NativeListRendererCell.swift +311 -0
  60. package/ios/NativeListRendererRegistry.swift +50 -0
  61. package/ios/NativeListResolvedText.swift +122 -0
  62. package/ios/NativeListRowHost.swift +63 -0
  63. package/ios/NativeListSectionHeaderRenderer.swift +708 -0
  64. package/ios/NativeListSystemRenderer.swift +568 -0
  65. package/ios/NativeListTextLabel.swift +42 -0
  66. package/ios/NativeListTextStyles.swift +236 -0
  67. package/ios/NativeListWalletGroupRenderer.swift +255 -0
  68. package/ios/RNCNativeListView.swift +111 -180
  69. package/lib/module/models.js +54 -0
  70. package/lib/module/validation.js +324 -26
  71. package/lib/module/web/NativeListWebEngine.js +148 -936
  72. package/lib/module/web/templates/ActionRowRenderer.js +97 -0
  73. package/lib/module/web/templates/ActivityRowRenderer.js +95 -0
  74. package/lib/module/web/templates/DataRowRenderer.js +145 -0
  75. package/lib/module/web/templates/IdentityRowRenderer.js +289 -0
  76. package/lib/module/web/templates/MarketRowRenderer.js +310 -0
  77. package/lib/module/web/templates/MediaTileRowRenderer.js +132 -0
  78. package/lib/module/web/templates/MessageRowRenderer.js +165 -0
  79. package/lib/module/web/templates/MetricCardRowRenderer.js +172 -0
  80. package/lib/module/web/templates/RailRowRenderer.js +98 -0
  81. package/lib/module/web/templates/RowContainerStyle.js +100 -0
  82. package/lib/module/web/templates/RowElements.js +93 -0
  83. package/lib/module/web/templates/RowRendererRegistry.js +70 -0
  84. package/lib/module/web/templates/RowText.js +121 -0
  85. package/lib/module/web/templates/RowVisual.js +43 -0
  86. package/lib/module/web/templates/SectionHeaderRowRenderer.js +146 -0
  87. package/lib/module/web/templates/SystemRowRenderer.js +161 -0
  88. package/lib/module/web/templates/WalletGroupRowRenderer.js +96 -0
  89. package/lib/typescript/src/models.d.ts +166 -13
  90. package/lib/typescript/src/web/NativeListWebEngine.d.ts +18 -16
  91. package/lib/typescript/src/web/templates/ActionRowRenderer.d.ts +15 -0
  92. package/lib/typescript/src/web/templates/ActivityRowRenderer.d.ts +15 -0
  93. package/lib/typescript/src/web/templates/DataRowRenderer.d.ts +15 -0
  94. package/lib/typescript/src/web/templates/IdentityRowRenderer.d.ts +16 -0
  95. package/lib/typescript/src/web/templates/MarketRowRenderer.d.ts +30 -0
  96. package/lib/typescript/src/web/templates/MediaTileRowRenderer.d.ts +15 -0
  97. package/lib/typescript/src/web/templates/MessageRowRenderer.d.ts +44 -0
  98. package/lib/typescript/src/web/templates/MetricCardRowRenderer.d.ts +15 -0
  99. package/lib/typescript/src/web/templates/RailRowRenderer.d.ts +28 -0
  100. package/lib/typescript/src/web/templates/RowContainerStyle.d.ts +9 -0
  101. package/lib/typescript/src/web/templates/RowElements.d.ts +38 -0
  102. package/lib/typescript/src/web/templates/RowRendererRegistry.d.ts +83 -0
  103. package/lib/typescript/src/web/templates/RowText.d.ts +9 -0
  104. package/lib/typescript/src/web/templates/RowVisual.d.ts +20 -0
  105. package/lib/typescript/src/web/templates/SectionHeaderRowRenderer.d.ts +15 -0
  106. package/lib/typescript/src/web/templates/SystemRowRenderer.d.ts +16 -0
  107. package/lib/typescript/src/web/templates/WalletGroupRowRenderer.d.ts +20 -0
  108. package/package.json +3 -3
  109. package/src/models.ts +281 -19
  110. package/src/validation.ts +501 -46
  111. package/src/web/NativeListWebEngine.ts +361 -1932
  112. package/src/web/templates/ActionRowRenderer.ts +123 -0
  113. package/src/web/templates/ActivityRowRenderer.ts +159 -0
  114. package/src/web/templates/DataRowRenderer.ts +212 -0
  115. package/src/web/templates/IdentityRowRenderer.ts +527 -0
  116. package/src/web/templates/MarketRowRenderer.ts +467 -0
  117. package/src/web/templates/MediaTileRowRenderer.ts +182 -0
  118. package/src/web/templates/MessageRowRenderer.ts +306 -0
  119. package/src/web/templates/MetricCardRowRenderer.ts +337 -0
  120. package/src/web/templates/RailRowRenderer.ts +136 -0
  121. package/src/web/templates/RowContainerStyle.ts +131 -0
  122. package/src/web/templates/RowElements.ts +160 -0
  123. package/src/web/templates/RowRendererRegistry.ts +111 -0
  124. package/src/web/templates/RowText.ts +181 -0
  125. package/src/web/templates/RowVisual.ts +115 -0
  126. package/src/web/templates/SectionHeaderRowRenderer.ts +243 -0
  127. package/src/web/templates/SystemRowRenderer.ts +282 -0
  128. package/src/web/templates/WalletGroupRowRenderer.ts +131 -0
  129. package/android/src/main/java/com/onekey/nativelist/NativeListRowView.kt +0 -4538
  130. package/ios/NativeListCell.swift +0 -4300
@@ -0,0 +1,234 @@
1
+ package com.margelo.nitro.nativelist
2
+
3
+ import android.graphics.Color
4
+ import android.graphics.Outline
5
+ import android.graphics.drawable.GradientDrawable
6
+ import android.view.Gravity
7
+ import android.view.View
8
+ import android.view.ViewOutlineProvider
9
+ import android.widget.FrameLayout
10
+ import android.widget.LinearLayout
11
+ import com.facebook.react.uimanager.ThemedReactContext
12
+ import org.json.JSONObject
13
+
14
+ internal class NativeListMediaTileRowView(context: ThemedReactContext) :
15
+ NativeListRendererRowView(context) {
16
+ private val picture = FrameLayout(context)
17
+ private val image = NativeListImageSlot(context)
18
+ private val network = NativeListImageSlot(context)
19
+ private val errorIcon = OneKeyIconView(context)
20
+ private val title = NativeListTextView(context)
21
+ private val subtitle = NativeListTextView(context)
22
+ private val badge = NativeListTextView(context)
23
+ private val metadata = LinearLayout(context).apply { orientation = VERTICAL }
24
+ private val subtitleLine =
25
+ LinearLayout(context).apply {
26
+ orientation = HORIZONTAL
27
+ gravity = Gravity.CENTER_VERTICAL
28
+ }
29
+ private val close = NativeListTextView(context)
30
+ private var imageStyle = JSONObject()
31
+ private var closeKey = ""
32
+ override val defaultCornerRadius = 16
33
+ override val showsSelection = false
34
+ override val pressChangesBackground = false
35
+ override val assetFields = listOf("image", "networkImage")
36
+
37
+ override fun horizontalWidth(item: NativeListItem) = dp(200)
38
+
39
+ override fun pressContent(pressed: Boolean) {
40
+ picture.alpha = if (pressed) 0.8f else 1f
41
+ }
42
+
43
+ init {
44
+ orientation = VERTICAL
45
+ picture.addView(image.view, FrameLayout.LayoutParams(-1, -1))
46
+ picture.addView(errorIcon)
47
+ picture.addView(badge)
48
+ subtitleLine.addView(subtitle, LayoutParams(0, -2, 1f))
49
+ subtitleLine.addView(network.view)
50
+ metadata.addView(subtitleLine, LayoutParams(-1, -2))
51
+ metadata.addView(title, LayoutParams(-1, -2))
52
+ addView(picture)
53
+ addView(metadata, LayoutParams(-1, -2))
54
+ addView(close, LayoutParams(-1, -2))
55
+ close.setOnClickListener { emitAction(closeKey, close, "mediaClose") }
56
+ close.gravity = Gravity.END
57
+ }
58
+
59
+ override fun bindContent(
60
+ item: NativeListItem,
61
+ theme: JSONObject?,
62
+ layout: String,
63
+ checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
64
+ ) {
65
+ val style = item.json.optJSONObject("style") ?: JSONObject()
66
+ imageStyle = style.optJSONObject("image") ?: JSONObject()
67
+ fun size(name: String, fallback: Int) =
68
+ if (style.has(name)) stylePx(style.optDouble(name)) else dp(fallback)
69
+ fun tint(name: String, fallback: String) =
70
+ parseNativeListColor(theme?.optString(name, fallback) ?: fallback)
71
+ setPadding(
72
+ size("horizontalPadding", 10),
73
+ size("verticalPadding", 10),
74
+ size("horizontalPadding", 10),
75
+ size("verticalPadding", 10),
76
+ )
77
+ gravity =
78
+ Gravity.START or
79
+ when (style.optJSONObject("container")?.optString("contentVerticalAlignment")) {
80
+ "bottom" -> Gravity.BOTTOM
81
+ "center" -> Gravity.CENTER_VERTICAL
82
+ else -> Gravity.TOP
83
+ }
84
+ NativeListResolvedText.resolve(
85
+ reactContext,
86
+ item.json.optString("title"),
87
+ style.optJSONObject("title"),
88
+ 16f,
89
+ "medium",
90
+ tint("primaryText", "#000000DF"),
91
+ 24,
92
+ 1,
93
+ sourceScale,
94
+ )
95
+ .bind(title)
96
+ NativeListResolvedText.resolve(
97
+ reactContext,
98
+ item.json.optString("subtitle"),
99
+ style.optJSONObject("subtitle"),
100
+ 12f,
101
+ "regular",
102
+ tint("secondaryText", "#0000009B"),
103
+ 16,
104
+ 1,
105
+ sourceScale,
106
+ )
107
+ .bind(subtitle)
108
+ NativeListResolvedText.resolve(
109
+ reactContext,
110
+ item.json.optJSONObject("badge")?.optString("text") ?: "",
111
+ style.optJSONObject("badge"),
112
+ 14f,
113
+ "regular",
114
+ parseNativeListColor("#FCFCFC"),
115
+ 0,
116
+ 1,
117
+ sourceScale,
118
+ )
119
+ .bind(badge)
120
+ if (style.optJSONObject("badge") == null) {
121
+ // Legacy badge text keeps the platform font padding and proportional digits.
122
+ badge.includeFontPadding = true
123
+ badge.fontFeatureSettings = null
124
+ }
125
+ if (!style.optJSONObject("badge").let { it?.has("alignment") == true })
126
+ badge.gravity = Gravity.CENTER
127
+ badge.setPadding(dp(8), 0, dp(8), 0)
128
+ badge.background =
129
+ GradientDrawable().apply {
130
+ setColor(parseNativeListColor("#000000DF"))
131
+ cornerRadius = dp(10).toFloat()
132
+ setStroke(dp(2), Color.WHITE)
133
+ }
134
+ badge.layoutParams = FrameLayout.LayoutParams(-2, dp(24), Gravity.END or Gravity.BOTTOM)
135
+ title.layoutParams = LayoutParams(-1, -2).apply { topMargin = size("lineGap", 0) }
136
+ metadata.layoutParams = LayoutParams(-1, -2).apply { topMargin = size("leadingGap", 8) }
137
+ (subtitle.layoutParams as LayoutParams).marginEnd = dp(8)
138
+ val state = item.json.optString("imageState")
139
+ errorIcon.visibility = if (state == "error") VISIBLE else GONE
140
+ errorIcon.iconName = "ImageSquareWavesOutline"
141
+ errorIcon.tintColor = parseNativeListColor("#00000044")
142
+ errorIcon.useSourceScale = sourceScale
143
+ errorIcon.layoutParams = FrameLayout.LayoutParams(dp(24), dp(24), Gravity.CENTER)
144
+ if (state != "empty" && state != "error" && item.json.has("image"))
145
+ image.bind(
146
+ item.json.getJSONObject("image"),
147
+ "${item.key}:media",
148
+ fit = imageStyle.optString("contentFit").takeIf { it.isNotEmpty() },
149
+ placeholder =
150
+ theme?.optString("strongBackground", "#0000000F")?.takeIf(String::isNotEmpty)
151
+ ?: "#0000000F",
152
+ )
153
+ else image.recycle()
154
+ // Legacy: only the empty/error states paint a slot fill; loaded images show
155
+ // their own placeholder without a white backing.
156
+ image.view.background =
157
+ when (state) {
158
+ "error" -> GradientDrawable().apply { setColor(tint("strongBackground", "#0000000F")) }
159
+ "empty" -> GradientDrawable().apply { setColor(Color.WHITE) }
160
+ else -> null
161
+ }
162
+ image.view.outlineProvider =
163
+ object : ViewOutlineProvider() {
164
+ override fun getOutline(view: View, outline: Outline) {
165
+ if (
166
+ imageStyle.optString("shape") == "circle" && !imageStyle.has("cornerRadius")
167
+ )
168
+ outline.setOval(0, 0, view.width, view.height)
169
+ else
170
+ outline.setRoundRect(
171
+ 0,
172
+ 0,
173
+ view.width,
174
+ view.height,
175
+ if (imageStyle.has("cornerRadius"))
176
+ stylePx(imageStyle.optDouble("cornerRadius")).toFloat()
177
+ else if (imageStyle.optString("shape") == "square") 0f
178
+ else dp(10).toFloat(),
179
+ )
180
+ }
181
+ }
182
+ image.view.clipToOutline = true
183
+ network.view.layoutParams = LayoutParams(dp(14), dp(14))
184
+ network.view.outlineProvider =
185
+ object : ViewOutlineProvider() {
186
+ override fun getOutline(view: View, outline: Outline) {
187
+ outline.setOval(0, 0, view.width, view.height)
188
+ }
189
+ }
190
+ network.view.clipToOutline = true
191
+ item.json.optJSONObject("networkImage")?.let { source ->
192
+ network.view.visibility = VISIBLE
193
+ network.view.alpha = 0f
194
+ network.bind(source, "${item.key}:network", variant = "network", round = true) { loaded
195
+ ->
196
+ network.view.alpha = if (loaded) 1f else 0f
197
+ }
198
+ }
199
+ ?: run {
200
+ network.recycle()
201
+ network.view.visibility = GONE
202
+ }
203
+ closeKey = item.json.optString("closeActionKey")
204
+ close.text = "×"
205
+ close.textSize = if (sourceScale) 22f else NativeListScale.font(resources, 22f)
206
+ close.setTextColor(tint("primaryText", "#000000DF"))
207
+ close.visibility = if (closeKey.isEmpty()) GONE else VISIBLE
208
+ }
209
+
210
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
211
+ val width =
212
+ if (imageStyle.has("width")) stylePx(imageStyle.optDouble("width"))
213
+ else
214
+ (MeasureSpec.getSize(widthMeasureSpec) - paddingLeft - paddingRight).coerceAtLeast(
215
+ 0
216
+ )
217
+ val height =
218
+ if (imageStyle.has("height")) stylePx(imageStyle.optDouble("height")) else width
219
+ picture.layoutParams = LayoutParams(width, height)
220
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
221
+ }
222
+
223
+ override fun recycleContent() {
224
+ image.recycle()
225
+ network.recycle()
226
+ closeKey = ""
227
+ picture.alpha = 1f
228
+ }
229
+
230
+ override fun disposeContent() {
231
+ image.dispose()
232
+ network.dispose()
233
+ }
234
+ }
@@ -0,0 +1,232 @@
1
+ package com.margelo.nitro.nativelist
2
+
3
+ import android.graphics.Color
4
+ import android.graphics.Outline
5
+ import android.graphics.drawable.GradientDrawable
6
+ import android.view.Gravity
7
+ import android.view.View
8
+ import android.view.ViewOutlineProvider
9
+ import android.widget.LinearLayout
10
+ import com.facebook.react.uimanager.ThemedReactContext
11
+ import kotlin.math.roundToInt
12
+ import org.json.JSONObject
13
+
14
+ internal object NativeListMessageRenderer {
15
+ data class Resolved(
16
+ val title: NativeListResolvedText,
17
+ val body: NativeListResolvedText,
18
+ val time: NativeListResolvedText,
19
+ val horizontalPadding: Int,
20
+ val verticalPadding: Int,
21
+ val bodyGap: Int,
22
+ val timeGap: Int,
23
+ val leadingGap: Int,
24
+ val imageWidth: Int,
25
+ val imageHeight: Int,
26
+ val imageStyle: JSONObject,
27
+ val leading: JSONObject?,
28
+ val thumbnail: JSONObject?,
29
+ val unread: Boolean,
30
+ val gravity: Int,
31
+ )
32
+
33
+ fun resolve(
34
+ context: ThemedReactContext,
35
+ item: NativeListItem,
36
+ theme: JSONObject?,
37
+ sourceScale: Boolean,
38
+ ): Resolved {
39
+ val style = item.json.optJSONObject("style") ?: JSONObject()
40
+ val density = context.resources.displayMetrics.density
41
+ fun dp(value: Int) =
42
+ if (sourceScale) (value * density).roundToInt()
43
+ else NativeListScale.dp(context.resources, value)
44
+ fun dimension(source: JSONObject, name: String, default: Int) =
45
+ if (source.has(name)) (source.optDouble(name) * density).roundToInt() else dp(default)
46
+ fun color(name: String, fallback: String) =
47
+ parseNativeListColor(theme?.optString(name, fallback) ?: fallback)
48
+ fun text(name: String, size: Float, weight: String, color: Int, height: Int, lines: Int) =
49
+ NativeListResolvedText.resolve(
50
+ context,
51
+ item.json.optString(name),
52
+ style.optJSONObject(name),
53
+ size,
54
+ weight,
55
+ color,
56
+ height,
57
+ lines,
58
+ sourceScale,
59
+ )
60
+ val image = style.optJSONObject("image") ?: JSONObject()
61
+ return Resolved(
62
+ text("title", 14f, "semibold", color("primaryText", "#000000DF"), 20, 2),
63
+ text(
64
+ "body",
65
+ 14f,
66
+ "regular",
67
+ color("secondaryText", "#0000009B"),
68
+ 20,
69
+ item.json.optInt("bodyLines", 3),
70
+ ),
71
+ text("time", 12f, "regular", color("disabledText", "#00000072"), 16, 1),
72
+ dimension(style, "horizontalPadding", 12),
73
+ dimension(style, "verticalPadding", 16),
74
+ dimension(style, "lineGap", 2),
75
+ dimension(style, "lineGap", 4),
76
+ dimension(style, "leadingGap", 12),
77
+ dimension(image, "width", 28),
78
+ dimension(image, "height", 28),
79
+ image,
80
+ item.json.optJSONObject("leading"),
81
+ item.json.optJSONObject("thumbnail"),
82
+ item.json.optBoolean("unread"),
83
+ when (style.optJSONObject("container")?.optString("contentVerticalAlignment")) {
84
+ "bottom" -> Gravity.BOTTOM
85
+ "center" -> Gravity.CENTER_VERTICAL
86
+ else -> Gravity.TOP
87
+ },
88
+ )
89
+ }
90
+
91
+ class Views(private val context: ThemedReactContext) {
92
+ val column = LinearLayout(context).apply { orientation = LinearLayout.VERTICAL }
93
+ val title = NativeListTextView(context)
94
+ val body = NativeListTextView(context)
95
+ val time = NativeListTextView(context)
96
+ private var leading: NativeListLeadingVisual? = null
97
+ private var thumbnail: NativeListImageSlot? = null
98
+
99
+ init {
100
+ listOf(title, body, time).forEach {
101
+ column.addView(
102
+ it,
103
+ LinearLayout.LayoutParams(
104
+ LinearLayout.LayoutParams.MATCH_PARENT,
105
+ LinearLayout.LayoutParams.WRAP_CONTENT,
106
+ ),
107
+ )
108
+ }
109
+ }
110
+
111
+ fun bind(
112
+ root: LinearLayout,
113
+ resolved: Resolved,
114
+ item: NativeListItem,
115
+ theme: JSONObject?,
116
+ sourceScale: Boolean,
117
+ ) {
118
+ resolved.title.bind(title)
119
+ resolved.body.bind(body)
120
+ resolved.time.bind(time)
121
+ root.gravity = resolved.gravity
122
+ root.clipChildren =
123
+ !(resolved.leading?.optString("kind") == "token" && resolved.leading.has("networkImage"))
124
+ root.setPadding(
125
+ resolved.horizontalPadding,
126
+ resolved.verticalPadding,
127
+ resolved.horizontalPadding,
128
+ resolved.verticalPadding,
129
+ )
130
+ resolved.leading?.let { visual ->
131
+ val host = leading ?: NativeListLeadingVisual(context).also { leading = it }
132
+ host.bind(visual, resolved.imageStyle, item.key, theme, resolved.unread, sourceScale)
133
+ val params =
134
+ LinearLayout.LayoutParams(resolved.imageWidth, resolved.imageHeight).apply {
135
+ marginEnd = resolved.leadingGap
136
+ }
137
+ if (host.parent == null) root.addView(host, 0, params) else host.layoutParams = params
138
+ }
139
+ ?: leading?.let {
140
+ it.recycle()
141
+ root.removeView(it)
142
+ }
143
+ if (column.parent == null)
144
+ root.addView(
145
+ column,
146
+ LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f),
147
+ )
148
+ body.layoutParams =
149
+ LinearLayout.LayoutParams(
150
+ LinearLayout.LayoutParams.MATCH_PARENT,
151
+ LinearLayout.LayoutParams.WRAP_CONTENT,
152
+ )
153
+ .apply { topMargin = resolved.bodyGap }
154
+ time.layoutParams =
155
+ LinearLayout.LayoutParams(
156
+ LinearLayout.LayoutParams.MATCH_PARENT,
157
+ LinearLayout.LayoutParams.WRAP_CONTENT,
158
+ )
159
+ .apply { topMargin = resolved.timeGap }
160
+ resolved.thumbnail?.let { image ->
161
+ val slot = thumbnail ?: NativeListImageSlot(context).also { thumbnail = it }
162
+ val dp: (Int) -> Int = {
163
+ if (sourceScale) (it * context.resources.displayMetrics.density).roundToInt()
164
+ else NativeListScale.dp(context.resources, it)
165
+ }
166
+ slot.view.outlineProvider =
167
+ object : ViewOutlineProvider() {
168
+ override fun getOutline(view: View, outline: Outline) {
169
+ outline.setRoundRect(0, 0, view.width, view.height, dp(6).toFloat())
170
+ }
171
+ }
172
+ slot.view.clipToOutline = true
173
+ slot.view.foreground =
174
+ GradientDrawable().apply {
175
+ setColor(Color.TRANSPARENT)
176
+ cornerRadius = dp(6).toFloat()
177
+ setStroke(
178
+ 1,
179
+ parseNativeListColor(theme?.optString("strongBackground", "#0000000F") ?: "#0000000F"),
180
+ )
181
+ }
182
+ val params = LinearLayout.LayoutParams(dp(64), dp(64)).apply { marginStart = dp(12) }
183
+ if (slot.view.parent == null) root.addView(slot.view, params)
184
+ else slot.view.layoutParams = params
185
+ slot.bind(
186
+ image,
187
+ "${item.key}:thumbnail",
188
+ placeholder = theme?.optString("strongBackground", "#0000000F") ?: "#0000000F",
189
+ )
190
+ }
191
+ ?: thumbnail?.let {
192
+ it.recycle()
193
+ root.removeView(it.view)
194
+ }
195
+ }
196
+
197
+ fun recycle() {
198
+ leading?.recycle()
199
+ thumbnail?.recycle()
200
+ }
201
+
202
+ fun dispose() {
203
+ leading?.dispose()
204
+ thumbnail?.dispose()
205
+ }
206
+ }
207
+ }
208
+
209
+ internal class NativeListMessageRowView(context: ThemedReactContext) :
210
+ NativeListRendererRowView(context) {
211
+ override val assetFields = listOf("leading", "thumbnail")
212
+ private val views = NativeListMessageRenderer.Views(context)
213
+
214
+ override fun bindContent(
215
+ item: NativeListItem,
216
+ theme: JSONObject?,
217
+ layout: String,
218
+ checkboxState: (NativeListItem, NativeSelectionTarget?, String) -> String,
219
+ ) {
220
+ views.bind(
221
+ this,
222
+ NativeListMessageRenderer.resolve(reactContext, item, theme, sourceScale),
223
+ item,
224
+ theme,
225
+ sourceScale,
226
+ )
227
+ }
228
+
229
+ override fun recycleContent() = views.recycle()
230
+
231
+ override fun disposeContent() = views.dispose()
232
+ }