@nadohq/mobile-client 0.65.0 → 0.67.0

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 (211) hide show
  1. package/README.md +202 -10
  2. package/dist/{MobileClient-CfVZXTcz.cjs → MobileClient-vuI5wlB_.cjs} +303 -21
  3. package/dist/MobileClient-vuI5wlB_.cjs.map +1 -0
  4. package/dist/MobileClient.cjs +1 -1
  5. package/dist/MobileClient.d.cts +175 -20
  6. package/dist/MobileClient.d.cts.map +1 -1
  7. package/dist/MobileClient.d.ts +175 -20
  8. package/dist/MobileClient.d.ts.map +1 -1
  9. package/dist/MobileClient.js +303 -21
  10. package/dist/MobileClient.js.map +1 -1
  11. package/dist/{MobileServerFailureError-BZVwlsMg.d.ts → MobileServerFailureError-BeCCNShj.d.cts} +2 -3
  12. package/dist/MobileServerFailureError-BeCCNShj.d.cts.map +1 -0
  13. package/dist/{MobileServerFailureError-CbtK8kD4.d.cts → MobileServerFailureError-hHv28weI.d.ts} +2 -3
  14. package/dist/MobileServerFailureError-hHv28weI.d.ts.map +1 -0
  15. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts +15 -0
  16. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.cts.map +1 -0
  17. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts +15 -0
  18. package/dist/MobileUnsupportedNotificationRouteError-DpiNBvvY.d.ts.map +1 -0
  19. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts → buildSignedMobileRequest-DcEMKU_4.d.cts} +2 -2
  20. package/dist/{buildSignedMobileRequest-C1v0flCR.d.cts.map → buildSignedMobileRequest-DcEMKU_4.d.cts.map} +1 -1
  21. package/dist/{buildSignedMobileRequest-UeeWdYGU.d.ts → buildSignedMobileRequest-DlTMvoXR.d.ts} +2 -2
  22. package/dist/{buildSignedMobileRequest-UeeWdYGU.d.ts.map → buildSignedMobileRequest-DlTMvoXR.d.ts.map} +1 -1
  23. package/dist/{canonicalize-5eeZrkzQ.d.ts → canonicalize-BOZlflI3.d.ts} +2 -2
  24. package/dist/canonicalize-BOZlflI3.d.ts.map +1 -0
  25. package/dist/{canonicalize-CBaZprTo.d.cts → canonicalize-BvCozzvJ.d.cts} +2 -2
  26. package/dist/canonicalize-BvCozzvJ.d.cts.map +1 -0
  27. package/dist/{clientTypes-_mEgzmNs.d.cts → clientTypes-KpHlCDGZ.d.cts} +341 -38
  28. package/dist/clientTypes-KpHlCDGZ.d.cts.map +1 -0
  29. package/dist/{clientTypes-C9dJufxk.d.ts → clientTypes-XRI41VdC.d.ts} +341 -38
  30. package/dist/clientTypes-XRI41VdC.d.ts.map +1 -0
  31. package/dist/dataMappers.cjs +257 -20
  32. package/dist/dataMappers.cjs.map +1 -1
  33. package/dist/dataMappers.d.cts +98 -15
  34. package/dist/dataMappers.d.cts.map +1 -1
  35. package/dist/dataMappers.d.ts +98 -15
  36. package/dist/dataMappers.d.ts.map +1 -1
  37. package/dist/dataMappers.js +242 -20
  38. package/dist/dataMappers.js.map +1 -1
  39. package/dist/{eip712MethodByType-D8jLoxDu.d.cts → eip712MethodByType-DyAmzH7b.d.cts} +2 -2
  40. package/dist/eip712MethodByType-DyAmzH7b.d.cts.map +1 -0
  41. package/dist/{eip712MethodByType-DcUh8h-l.d.ts → eip712MethodByType-qSSAF6i2.d.ts} +2 -2
  42. package/dist/eip712MethodByType-qSSAF6i2.d.ts.map +1 -0
  43. package/dist/feed-BrgkFqP3.d.cts +23 -0
  44. package/dist/feed-BrgkFqP3.d.cts.map +1 -0
  45. package/dist/feed-DcbhLoo1.d.ts +23 -0
  46. package/dist/feed-DcbhLoo1.d.ts.map +1 -0
  47. package/dist/index-BVVgDSdq.d.cts +1 -0
  48. package/dist/index-BVVgDSdq.d.ts +1 -0
  49. package/dist/index.cjs +8 -2
  50. package/dist/index.d.cts +14 -16
  51. package/dist/index.d.ts +14 -16
  52. package/dist/index.js +4 -3
  53. package/dist/{mobileErrorCodes-CmjYEIYm.d.ts → mobileErrorCodes-CdwX6QTO.d.cts} +8 -3
  54. package/dist/mobileErrorCodes-CdwX6QTO.d.cts.map +1 -0
  55. package/dist/{mobileErrorCodes-CmjYEIYm.d.cts → mobileErrorCodes-CdwX6QTO.d.ts} +8 -3
  56. package/dist/mobileErrorCodes-CdwX6QTO.d.ts.map +1 -0
  57. package/dist/{payloadHash-DhHmgRqL.d.cts → payloadHash-BgGSZock.d.cts} +2 -2
  58. package/dist/payloadHash-BgGSZock.d.cts.map +1 -0
  59. package/dist/{payloadHash-E1_8fpLW.d.ts → payloadHash-Bi6upCLQ.d.ts} +2 -2
  60. package/dist/payloadHash-Bi6upCLQ.d.ts.map +1 -0
  61. package/dist/{serverExecuteTypes-BwWGspOg.d.ts → serverExecuteTypes-DuCWe_H_.d.cts} +23 -14
  62. package/dist/serverExecuteTypes-DuCWe_H_.d.cts.map +1 -0
  63. package/dist/{serverExecuteTypes-B-OJok19.d.cts → serverExecuteTypes-usJ6HMJv.d.ts} +23 -14
  64. package/dist/serverExecuteTypes-usJ6HMJv.d.ts.map +1 -0
  65. package/dist/{serverModelTypes-CiA-dy1R.d.ts → serverModelTypes-CTHMNIQS.d.cts} +170 -13
  66. package/dist/serverModelTypes-CTHMNIQS.d.cts.map +1 -0
  67. package/dist/{serverModelTypes-CiA-dy1R.d.cts → serverModelTypes-CTHMNIQS.d.ts} +170 -13
  68. package/dist/serverModelTypes-CTHMNIQS.d.ts.map +1 -0
  69. package/dist/{serverResponseGuards-CShiXpHT.d.ts → serverResponseGuards-CzmldAad.d.cts} +2 -2
  70. package/dist/{serverResponseGuards-BW69PS7O.d.cts.map → serverResponseGuards-CzmldAad.d.cts.map} +1 -1
  71. package/dist/{serverResponseGuards-BW69PS7O.d.cts → serverResponseGuards-Dvkbjp2_.d.ts} +2 -2
  72. package/dist/{serverResponseGuards-CShiXpHT.d.ts.map → serverResponseGuards-Dvkbjp2_.d.ts.map} +1 -1
  73. package/dist/signing/buildSignedMobileRequest.d.cts +1 -1
  74. package/dist/signing/buildSignedMobileRequest.d.ts +1 -1
  75. package/dist/signing/canonicalize.cjs +53 -2
  76. package/dist/signing/canonicalize.cjs.map +1 -1
  77. package/dist/signing/canonicalize.d.cts +1 -1
  78. package/dist/signing/canonicalize.d.ts +1 -1
  79. package/dist/signing/canonicalize.js +53 -2
  80. package/dist/signing/canonicalize.js.map +1 -1
  81. package/dist/signing/eip712MethodByType.cjs +7 -0
  82. package/dist/signing/eip712MethodByType.cjs.map +1 -1
  83. package/dist/signing/eip712MethodByType.d.cts +1 -1
  84. package/dist/signing/eip712MethodByType.d.ts +1 -1
  85. package/dist/signing/eip712MethodByType.js +7 -0
  86. package/dist/signing/eip712MethodByType.js.map +1 -1
  87. package/dist/signing/index.d.cts +5 -5
  88. package/dist/signing/index.d.ts +5 -5
  89. package/dist/signing/payloadHash.cjs +1 -1
  90. package/dist/signing/payloadHash.d.cts +1 -1
  91. package/dist/signing/payloadHash.d.ts +1 -1
  92. package/dist/signing/payloadHash.js +1 -1
  93. package/dist/signing/types.d.cts +1 -1
  94. package/dist/signing/types.d.ts +1 -1
  95. package/dist/types/MobileServerFailureError.d.cts +1 -1
  96. package/dist/types/MobileServerFailureError.d.ts +1 -1
  97. package/dist/types/MobileUnsupportedNotificationRouteError.cjs +19 -0
  98. package/dist/types/MobileUnsupportedNotificationRouteError.cjs.map +1 -0
  99. package/dist/types/MobileUnsupportedNotificationRouteError.d.cts +2 -0
  100. package/dist/types/MobileUnsupportedNotificationRouteError.d.ts +2 -0
  101. package/dist/types/MobileUnsupportedNotificationRouteError.js +18 -0
  102. package/dist/types/MobileUnsupportedNotificationRouteError.js.map +1 -0
  103. package/dist/types/clientTypes.d.cts +2 -2
  104. package/dist/types/clientTypes.d.ts +2 -2
  105. package/dist/types/index.cjs +4 -0
  106. package/dist/types/index.d.cts +8 -9
  107. package/dist/types/index.d.ts +8 -9
  108. package/dist/types/index.js +3 -2
  109. package/dist/types/mobileErrorCodes.cjs +11 -1
  110. package/dist/types/mobileErrorCodes.cjs.map +1 -1
  111. package/dist/types/mobileErrorCodes.d.cts +1 -1
  112. package/dist/types/mobileErrorCodes.d.ts +1 -1
  113. package/dist/types/mobileErrorCodes.js +11 -1
  114. package/dist/types/mobileErrorCodes.js.map +1 -1
  115. package/dist/types/serverBaseTypes.d.cts +1 -1
  116. package/dist/types/serverBaseTypes.d.ts +1 -1
  117. package/dist/types/serverExecuteTypes.d.cts +2 -2
  118. package/dist/types/serverExecuteTypes.d.ts +2 -2
  119. package/dist/types/serverModelTypes.cjs +15 -0
  120. package/dist/types/serverModelTypes.cjs.map +1 -1
  121. package/dist/types/serverModelTypes.d.cts +2 -2
  122. package/dist/types/serverModelTypes.d.ts +2 -2
  123. package/dist/types/serverModelTypes.js +14 -1
  124. package/dist/types/serverModelTypes.js.map +1 -1
  125. package/dist/types/serverQueryTypes.d.cts +2 -2
  126. package/dist/types/serverQueryTypes.d.ts +2 -2
  127. package/dist/types/serverRequestTypes.d.cts +2 -2
  128. package/dist/types/serverRequestTypes.d.ts +2 -2
  129. package/dist/types-CByl_NyJ.d.ts +825 -0
  130. package/dist/types-CByl_NyJ.d.ts.map +1 -0
  131. package/dist/types-CR5iaKR4.d.cts +825 -0
  132. package/dist/types-CR5iaKR4.d.cts.map +1 -0
  133. package/dist/utils/buildMobileFeedImageMultipart.cjs +31 -0
  134. package/dist/utils/buildMobileFeedImageMultipart.cjs.map +1 -0
  135. package/dist/utils/buildMobileFeedImageMultipart.d.cts +15 -0
  136. package/dist/utils/buildMobileFeedImageMultipart.d.cts.map +1 -0
  137. package/dist/utils/buildMobileFeedImageMultipart.d.ts +15 -0
  138. package/dist/utils/buildMobileFeedImageMultipart.d.ts.map +1 -0
  139. package/dist/utils/buildMobileFeedImageMultipart.js +30 -0
  140. package/dist/utils/buildMobileFeedImageMultipart.js.map +1 -0
  141. package/dist/utils/feed.cjs +12 -0
  142. package/dist/utils/feed.cjs.map +1 -1
  143. package/dist/utils/feed.d.cts +2 -2
  144. package/dist/utils/feed.d.ts +2 -2
  145. package/dist/utils/feed.js +11 -1
  146. package/dist/utils/feed.js.map +1 -1
  147. package/dist/utils/index.cjs +3 -1
  148. package/dist/utils/index.d.cts +3 -3
  149. package/dist/utils/index.d.ts +3 -3
  150. package/dist/utils/index.js +2 -2
  151. package/dist/utils/serverResponseGuards.d.cts +1 -1
  152. package/dist/utils/serverResponseGuards.d.ts +1 -1
  153. package/package.json +3 -3
  154. package/src/MobileClient.ts +444 -19
  155. package/src/dataMappers.test.ts +511 -6
  156. package/src/dataMappers.ts +325 -24
  157. package/src/feedImages.test.ts +309 -0
  158. package/src/notifications.test.ts +950 -0
  159. package/src/signing/canonicalize.ts +72 -0
  160. package/src/signing/eip712MethodByType.ts +10 -0
  161. package/src/signing/signing.test.ts +67 -1
  162. package/src/signing/types.ts +55 -9
  163. package/src/socialFeed.test.ts +63 -2
  164. package/src/types/MobileUnsupportedNotificationRouteError.ts +16 -0
  165. package/src/types/clientTypes.ts +427 -36
  166. package/src/types/index.ts +1 -0
  167. package/src/types/mobileErrorCodes.ts +10 -0
  168. package/src/types/serverExecuteTypes.ts +22 -10
  169. package/src/types/serverModelTypes.ts +216 -11
  170. package/src/types/serverQueryTypes.ts +106 -9
  171. package/src/types/serverRequestTypes.ts +114 -17
  172. package/src/utils/buildMobileFeedImageMultipart.test.ts +45 -0
  173. package/src/utils/buildMobileFeedImageMultipart.ts +36 -0
  174. package/src/utils/feed.ts +16 -0
  175. package/dist/MobileClient-CfVZXTcz.cjs.map +0 -1
  176. package/dist/MobileServerFailureError-BZVwlsMg.d.ts.map +0 -1
  177. package/dist/MobileServerFailureError-CbtK8kD4.d.cts.map +0 -1
  178. package/dist/canonicalize-5eeZrkzQ.d.ts.map +0 -1
  179. package/dist/canonicalize-CBaZprTo.d.cts.map +0 -1
  180. package/dist/clientTypes-C9dJufxk.d.ts.map +0 -1
  181. package/dist/clientTypes-_mEgzmNs.d.cts.map +0 -1
  182. package/dist/eip712MethodByType-D8jLoxDu.d.cts.map +0 -1
  183. package/dist/eip712MethodByType-DcUh8h-l.d.ts.map +0 -1
  184. package/dist/feed-DTJlsg8W.d.cts +0 -14
  185. package/dist/feed-DTJlsg8W.d.cts.map +0 -1
  186. package/dist/feed-DTJlsg8W.d.ts +0 -14
  187. package/dist/feed-DTJlsg8W.d.ts.map +0 -1
  188. package/dist/mobileErrorCodes-CmjYEIYm.d.cts.map +0 -1
  189. package/dist/mobileErrorCodes-CmjYEIYm.d.ts.map +0 -1
  190. package/dist/payloadHash-DhHmgRqL.d.cts.map +0 -1
  191. package/dist/payloadHash-E1_8fpLW.d.ts.map +0 -1
  192. package/dist/serverBaseTypes-B5J1_TyC.d.cts +0 -25
  193. package/dist/serverBaseTypes-B5J1_TyC.d.cts.map +0 -1
  194. package/dist/serverBaseTypes-DnLzsdm-.d.ts +0 -25
  195. package/dist/serverBaseTypes-DnLzsdm-.d.ts.map +0 -1
  196. package/dist/serverExecuteTypes-B-OJok19.d.cts.map +0 -1
  197. package/dist/serverExecuteTypes-BwWGspOg.d.ts.map +0 -1
  198. package/dist/serverModelTypes-CiA-dy1R.d.cts.map +0 -1
  199. package/dist/serverModelTypes-CiA-dy1R.d.ts.map +0 -1
  200. package/dist/serverQueryTypes-CJcjLB7n.d.ts +0 -283
  201. package/dist/serverQueryTypes-CJcjLB7n.d.ts.map +0 -1
  202. package/dist/serverQueryTypes-V1f9yE7L.d.cts +0 -283
  203. package/dist/serverQueryTypes-V1f9yE7L.d.cts.map +0 -1
  204. package/dist/serverRequestTypes-CRlkNSu5.d.cts +0 -270
  205. package/dist/serverRequestTypes-CRlkNSu5.d.cts.map +0 -1
  206. package/dist/serverRequestTypes-DZoOhV2o.d.ts +0 -270
  207. package/dist/serverRequestTypes-DZoOhV2o.d.ts.map +0 -1
  208. package/dist/types-BiRmH5Iv.d.cts +0 -108
  209. package/dist/types-BiRmH5Iv.d.cts.map +0 -1
  210. package/dist/types-DoJuq0Ij.d.ts +0 -108
  211. package/dist/types-DoJuq0Ij.d.ts.map +0 -1
package/README.md CHANGED
@@ -50,7 +50,11 @@ await mobile.setUsername({
50
50
  `getNotificationPreferences`, `getRegisteredWallet`, `getForYouFeed`, `getTopTradersFeed`,
51
51
  `getFriendsFeed`, `getProfileFeed`, `getMarketFeed`, `getPositionFeed`, `getFeedItem`, `getFeedUpdates`,
52
52
  `getSelfFeedPositions`, `resolveFeedPosition`, `resolveFeedAction`, `getFeedChartDrawing`, `getFeedComments`,
53
- `getPositionHistory`, `getPositionChart`, `getPositionWatchers`.
53
+ `getPositionHistory`, `getPositionHistoryAll`, `getPositionThoughts`, `getPositionChart`, `getPositionWatchers`.
54
+
55
+ `getNotificationRegistrationV2` and `getNotificationSettingsV2` use an active Expo token as their credential,
56
+ not a wallet signature. Before displaying wallet settings, compare the wallet returned by `getRegisteredWallet`
57
+ with the connected wallet (case-insensitively).
54
58
 
55
59
  `getProfiles` is the only profile route: it batches up to 25 subaccounts, returns them in the requested order,
56
60
  and carries follower totals (`include.followCounts`) and a follow summary (`include.followSummary.viewAs`) as
@@ -70,10 +74,11 @@ page is ordered by the listed relationship alone and `isFollowing` is absent rat
70
74
 
71
75
  `getFeed` returns the global trade feed. Session-based feeds include `getForYouFeed`, `getTopTradersFeed`,
72
76
  and `getFriendsFeed` (requires `viewAs`), plus `getProfileFeed`, `getMarketFeed`, and `getPositionFeed`.
73
- `getFeedItem` reads one item; `getFeedUpdates` polls session patches using the returned `viewerToken`.
74
- All are unsigned. Profile, position, and item reads include the `viewAs` account's own hidden content under
75
- `account_view`; the viewing account is not proof of wallet ownership. Prices are decimal `BigNumber` values;
76
- quantities, notionals, and PnL retain x18 units. See method and parameter JSDoc for session and polling rules.
77
+ `getFeedItem` reads one item without counting a view. Report views independently with `recordFeedViews`. `getFeedUpdates` polls session patches
78
+ using the returned `viewerToken`. All are unsigned. Profile, position, and item reads include the `viewAs`
79
+ account's own hidden content under `account_view`; the viewing account is not proof of wallet ownership.
80
+ Prices are decimal `BigNumber` values; quantities, notionals, and PnL retain x18 units. See method and
81
+ parameter JSDoc for session and polling rules.
77
82
 
78
83
  `getSelfFeedPositions`, `resolveFeedPosition`, and `resolveFeedAction` require `viewAs` and return eligibility
79
84
  hints for that account, not authenticated permissions. `getFeedComments` supports anchored, bidirectional
@@ -85,20 +90,207 @@ and a position-mode session for navigation and updates. `getPositionChart` retur
85
90
  keeping execution and liquidation Oracle prices separate. `getPositionWatchers` reads visitor counts without
86
91
  registering presence. Watching account previews are self-reported, not proof of account ownership.
87
92
 
93
+ ### Position history and thoughts
94
+
95
+ `getPositionHistory` and `getPositionHistoryAll` return `owner`, the current position owner's full profile,
96
+ even for an empty timeline. Do not infer ownership from a take or the viewing account.
97
+ Position patches do not refresh owner information; query history or profiles again.
98
+
99
+ `getPositionHistoryAll` returns only the most recent 500 actions, ordered by last event time and action UUID
100
+ descending. It excludes authored content: `relatedTake` is null and `summary.takeCount` is zero, even when
101
+ thoughts exist. `summary.actionCount` counts this response's scope. `nextCursor: null` means this endpoint
102
+ does not paginate, not that all history was returned. Use `getPositionHistory` with a fresh first request
103
+ for complete history. Each call creates a new session; only `viewerToken` may be reused.
104
+ Limit, cursor, and sessionId are not accepted.
105
+
106
+ `getPositionThoughts` returns only published posts/trade cards with both nonblank text and a saved drawing.
107
+ Uploaded/external images do not qualify. It returns full parsed drawing data and keeps descending first
108
+ drawing creation time / item UUID order. Do not sort by displayAtMs or updatedAtMs. Changing an existing
109
+ drawing does not move it; saved data need not contain visible strokes.
110
+
111
+ ```ts
112
+ const first = await mobile.getPositionThoughts({ positionId, limit: 20 });
113
+ if (first.nextCursor !== null) {
114
+ const next = await mobile.getPositionThoughts({
115
+ positionId,
116
+ limit: 20,
117
+ sessionId: first.readSession.sessionId,
118
+ viewerToken: first.readSession.viewerToken,
119
+ cursor: first.nextCursor,
120
+ });
121
+ }
122
+ ```
123
+
124
+ There is no 500-item total cap for thoughts. Continue until nextCursor is null, even when a page is empty.
125
+ Keep positionId, limit, and credentials unchanged; omitted viewAs inherits the original perspective.
126
+ Thoughts sessions cannot be exchanged with history/feed sessions. New eligible content requires refreshing.
127
+ Fully delivered items support existing detail, drawing, image, and update APIs with the returned session.
128
+ A 6328 error means the initial directory exceeded capacity; do not automatically retry the same query.
129
+ A 6312 error requires a fresh session. Neither condition represents a successfully truncated result.
130
+
88
131
  ### Public Executes
89
132
 
90
- `unregisterExpoToken`, `updateNotificationPreferences`. These are authenticated by possession of an active Expo
133
+ `unregisterExpoToken`, `updateNotificationPreferences`, `updateNotificationSettingsV2`. These are authenticated by possession of an active Expo
91
134
  push token rather than a wallet signature, so they still work at logout when a signature may be unobtainable.
92
135
 
93
- `recordFeedViews` and `setPositionPresence` use reading-session credentials rather than wallet signatures.
94
- See method and parameter JSDoc for exposure reporting and page-scoped presence rules.
136
+ `recordFeedViews` accepts `itemIds`, an array of 1–50 canonical lowercase UUIDs. Posts, trade cards,
137
+ comments, and replies can be mixed; only the supplied IDs increment, without incrementing their parents.
138
+ It sends only `type` and `item_ids` to `/public_execute`, without a wallet signature or reading session,
139
+ and returns `{ status: 'success' }`. Same-request duplicates count once, but another request increments
140
+ again. Missing, deleted, unpublished, excluded, and unsupported targets are skipped; even an entirely
141
+ skipped batch returns success. Invalid fields, UUIDs, or array sizes reject the entire batch.
142
+
143
+ ```ts
144
+ await mobile.recordFeedViews({ itemIds: [postId, commentId, replyId] });
145
+ ```
146
+
147
+ This operation is **not idempotent**: retrying after an uncertain network failure may count twice.
148
+ The SDK does not automatically retry. Read current counts from `interaction.viewCount` in details,
149
+ comment groups and their `replies`, previews, or session updates; the report returns no per-item counts.
150
+
151
+ `setPositionPresence` still requires reading-session credentials and a page-scoped `viewSessionId`.
152
+ Pagination, updates, and protected image reads also keep their existing session rules. Fetch uploaded
153
+ image bytes with `getFeedImageBytes` using the original credentials and paths from image metadata.
154
+ Legitimate session reads renew the idle expiration; use the latest `expiresAtMs`. View reports neither
155
+ require nor renew a reading session.
156
+
157
+ ### Comment groups
158
+
159
+ `getFeedComments` returns top-level `comments`, each with a complete flat `replies` array containing all
160
+ readable descendants in the thread snapshot. The `limit` (default 20, range 1–50) counts top-level groups;
161
+ descendants do not consume the limit and do not have separate cursors or nested `replies` arrays.
162
+ Top-level comments are newest first; descendants are oldest first with depth then UUID breaking timestamp
163
+ ties. Original parent relationships remain in `replyToItemId` and `replyToAuthor`.
164
+
165
+ `totalCount` counts all readable replies in the thread snapshot; `topLevelCount` counts only top-level
166
+ comments. Neither is limited to the returned page. An `anchorReplyId` at any depth returns its top-level
167
+ group and is echoed in the response. Forward/backward cursors never split a group. An unreadable ancestor
168
+ also makes its descendants unreadable. New replies at any depth require refreshing; updates only modify
169
+ already delivered content.
170
+
171
+ ```ts
172
+ const page = await mobile.getFeedComments({ rootItemId, limit: 20 });
173
+ for (const group of page.comments) {
174
+ // Render the top-level comment, followed by group.replies in the returned order.
175
+ }
176
+ ```
177
+
178
+ Comment previews remain individual replies. A preview's `nextCursor` opens the first complete group page
179
+ in the same snapshot; replace the preview with that page instead of prepending preview items.
180
+
181
+ ### Feed image reads
182
+
183
+ For React Native `Image` or expo-image, use `getFeedImageRequest` to pass the authenticated
184
+ URL and headers directly without converting bytes to base64:
185
+
186
+ ```ts
187
+ import { isMobileSocialFeedUploadedImage } from '@nadohq/mobile-client';
188
+
189
+ const source = isMobileSocialFeedUploadedImage(image)
190
+ ? (() => {
191
+ const { url, headers } = mobile.getFeedImageRequest({
192
+ path: image.url,
193
+ sessionId: readSession.sessionId,
194
+ viewerToken: readSession.viewerToken,
195
+ });
196
+ return { uri: url, headers };
197
+ })()
198
+ : { uri: image.url }; // External URL: never attach Mobile session headers.
199
+ ```
95
200
 
96
201
  ### Signed Executes
97
202
 
98
203
  `setUsername`, `setBio`, `createAvatarUpload`, `setAvatar`, `setPrivateMode`, `registerExpoToken`, `setFollow`,
99
- `createFeedPost`, `createFeedComment`, `createFeedChartDrawing`, `updateFeedChartDrawing`, `setFeedLike`.
204
+ `createFeedPost`, `createFeedComment`, `createFeedPostImageUpload`, `createFeedCommentImageUpload`,
205
+ `createFeedChartDrawing`, `updateFeedChartDrawing`, `setFeedLike`, `registerExpoTokenV2`,
206
+ `updateNotificationSubscriptions`, `recordProductEvents`.
207
+
208
+ Upload images one at a time with the multipart helpers (≤ 2 MiB whole request), then pass ordered ready
209
+ `imageIds` to `createFeedPost` / `createFeedComment`. The SDK throws `RangeError` before requesting a signature or sending a request
210
+ that exceeds `MOBILE_FEED_IMAGE_MAX_REQUEST_BYTES`, including the signed payload and multipart framing.
211
+ Unconfigured image storage returns `SOCIAL_FEED_DATA_UNAVAILABLE` (6320, HTTP 503).
212
+ Posts still require nonblank text or a drawing; comments
213
+ still require nonblank text. See method and parameter JSDoc for publication, retries, mention offsets, drawing
214
+ data, and mutation ordering.
215
+
216
+ Posts and comments also accept `imageUrls`, up to ten HTTP/HTTPS URLs independent of the nine uploaded
217
+ images. URLs must include a hostname and contain no whitespace/control characters. Omit the field or pass
218
+ `[]`; null and the old wire field `image_url` are rejected. The SDK signs `image_urls` after `image_ids`.
219
+ Keep original URL strings, array order, and field presence unchanged on retries: omission and `[]` hash
220
+ differently. Coordinate this request change with the service version; old `image_url` retries are incompatible.
221
+
222
+ Responses keep one `images` array: upload metadata first, then URL-only objects in submitted order.
223
+ `MobileSocialFeedImage` represents uploaded images or external URLs; narrow with
224
+ `isMobileSocialFeedUploadedImage` before accessing upload metadata.
225
+ Direct upload methods return `MobileSocialFeedUploadedImage` with all metadata. Load external URLs directly
226
+ without Mobile session headers.
227
+
228
+ ### Signed Queries
229
+
230
+ `getNotificationSubscriptions` and `getNotificationInboxV2` use wallet authentication on `/query`.
231
+ They do not require an Expo token or system push permission. The configured linked signer is supported.
232
+
233
+ ### Notifications V2
234
+
235
+ The notification v2 operations are available on `MobileClient`, including `client.context.mobileClient` from
236
+ `@nadohq/client`. Settings, capabilities, subscriptions, inbox data, and product events use camelCase SDK models.
237
+ The SDK maps requests to snake_case wire fields and removes the success envelope from read results.
238
+
239
+ ```ts
240
+ const auth = {
241
+ subaccountOwner: walletClient.account.address,
242
+ subaccountName: 'default',
243
+ chainId: ink.id,
244
+ verifyingAddr: '0x...' as const,
245
+ };
246
+
247
+ await mobile.registerExpoTokenV2({
248
+ ...auth,
249
+ expoToken,
250
+ platform: 'ios',
251
+ capabilities: {
252
+ payloadVersions: [2],
253
+ typeIds: ['order_fill', 'new_follower'],
254
+ routes: ['portfolio', 'profile'],
255
+ },
256
+ });
257
+ const registration = await mobile.getNotificationRegistrationV2({ expoToken });
258
+ const current = await mobile.getNotificationSettingsV2({ expoToken });
259
+ const saved = await mobile.updateNotificationSettingsV2({
260
+ expoToken,
261
+ expectedRevision: current.revision,
262
+ settings: { ...current.settings, positionThresholds: [25] },
263
+ });
264
+
265
+ const subscriptions = await mobile.getNotificationSubscriptions(auth);
266
+ await mobile.updateNotificationSubscriptions({
267
+ ...auth,
268
+ update: {
269
+ expectedRevision: subscriptions.revision,
270
+ market: { productId: 2, subscribed: true },
271
+ },
272
+ });
273
+ const updatedSubscriptions = await mobile.getNotificationSubscriptions(auth);
274
+ const inbox = await mobile.getNotificationInboxV2({ ...auth, limit: 20 });
275
+ ```
100
276
 
101
- See method and parameter JSDoc for publication, retries, mention offsets, drawing data, and mutation ordering.
277
+ - Declare only types/routes the application handles. Re-registration advances the generation and cancels
278
+ old pending deliveries; calling legacy `registerExpoToken` resets capabilities to v1.
279
+ - Settings replacement requires every field. Settings and subscription revisions are independent.
280
+ On `MOBILE_ERROR_CODES.NOTIFICATION_REVISION_CONFLICT` (6203), re-read and merge only the current user change.
281
+ - A subscription update selects exactly one operation; omit the other operations and the SDK sends them as `null`. Successful subscription
282
+ updates return only status, so re-query even after repeated ON/OFF. Favorites import merges once, never replaces.
283
+ - Enabling a bell requires following the target. Unfollowing clears its bell without advancing the subscription
284
+ revision; refresh subscriptions after follow mutations. Bells are Identity-scoped; markets are wallet-scoped.
285
+ Pass `traderBell.subaccount` as a `Subaccount` (`subaccountOwner` and `subaccountName`); the SDK encodes it for signing.
286
+ - Inbox pages are accepted intents, not delivery/read receipts. There is no title/body, unread count, mark-read,
287
+ or delete operation. Do not use the unimplemented legacy `notification_history` route.
288
+ An unknown destination rejects the page with `MobileUnsupportedNotificationRouteError`.
289
+ - `recordProductEvents({ ...auth, events })` accepts 1-50 events atomically. Provide the fields required by `eventName`,
290
+ actual millisecond timestamps, and stable `clientEventId` UUIDs across retries. The SDK fills unused wire fields with `null`. Supported
291
+ names are `notif_opened`, `market_viewed`, `chart_opened`, and `search`; obtain analytics consent first.
292
+ - Legacy preferences remain supported and synchronize overlapping v2 fields. Keep legacy push parsing because
293
+ order notifications may use a v1 fallback even for v2 registrations.
102
294
 
103
295
  ### Linked Signers
104
296