@hsuite/native-connect-angular 1.0.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 (276) hide show
  1. package/README.md +48 -0
  2. package/USAGE_EXAMPLES.md +476 -0
  3. package/assets/wallets/extension.svg +7 -0
  4. package/assets/wallets/hashpack.svg +6 -0
  5. package/assets/wallets/hsuite.svg +11 -0
  6. package/assets/wallets/kabila.svg +11 -0
  7. package/assets/wallets/walletconnect.svg +13 -0
  8. package/coverage/base.css +224 -0
  9. package/coverage/block-navigation.js +87 -0
  10. package/coverage/coverage-summary.json +50 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +476 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +476 -0
  17. package/coverage/lcov-report/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  18. package/coverage/lcov-report/lib/components/account-selector/account-actions/index.html +116 -0
  19. package/coverage/lcov-report/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  20. package/coverage/lcov-report/lib/components/account-selector/account-filter/index.html +116 -0
  21. package/coverage/lcov-report/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  22. package/coverage/lcov-report/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  23. package/coverage/lcov-report/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  24. package/coverage/lcov-report/lib/components/account-selector/account-list/index.html +116 -0
  25. package/coverage/lcov-report/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  26. package/coverage/lcov-report/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  27. package/coverage/lcov-report/lib/components/account-selector/index.html +161 -0
  28. package/coverage/lcov-report/lib/components/wallet-account-display/index.html +116 -0
  29. package/coverage/lcov-report/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  30. package/coverage/lcov-report/lib/components/wallet-connect-button/index.html +116 -0
  31. package/coverage/lcov-report/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  32. package/coverage/lcov-report/lib/components/wallet-connect-prompt/index.html +116 -0
  33. package/coverage/lcov-report/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  34. package/coverage/lcov-report/lib/components/wallet-connected-guard/index.html +116 -0
  35. package/coverage/lcov-report/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  36. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  37. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  38. package/coverage/lcov-report/lib/components/wallet-connection-modal/index.html +116 -0
  39. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  40. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  41. package/coverage/lcov-report/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  42. package/coverage/lcov-report/lib/components/wallet-session-display/index.html +116 -0
  43. package/coverage/lcov-report/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  44. package/coverage/lcov-report/lib/components/wallet-transaction-status/index.html +116 -0
  45. package/coverage/lcov-report/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  46. package/coverage/lcov-report/lib/directives/index.html +146 -0
  47. package/coverage/lcov-report/lib/directives/wallet-connected.directive.ts.html +670 -0
  48. package/coverage/lcov-report/lib/directives/wallet-context.directive.ts.html +547 -0
  49. package/coverage/lcov-report/lib/directives/wallet-events.directive.ts.html +781 -0
  50. package/coverage/lcov-report/lib/hsuite-wallet.module.ts.html +715 -0
  51. package/coverage/lcov-report/lib/index.html +116 -0
  52. package/coverage/lcov-report/lib/models/connection-config.model.ts.html +280 -0
  53. package/coverage/lcov-report/lib/models/index.html +131 -0
  54. package/coverage/lcov-report/lib/models/provider-types.ts.html +577 -0
  55. package/coverage/lcov-report/lib/providers/base-wallet-provider.ts.html +1138 -0
  56. package/coverage/lcov-report/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  57. package/coverage/lcov-report/lib/providers/hsuite-native/index.html +116 -0
  58. package/coverage/lcov-report/lib/providers/hsuite-native-provider.ts.html +2347 -0
  59. package/coverage/lcov-report/lib/providers/index.html +146 -0
  60. package/coverage/lcov-report/lib/providers/p2p-native/index.html +131 -0
  61. package/coverage/lcov-report/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  62. package/coverage/lcov-report/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  63. package/coverage/lcov-report/lib/providers/wallet-error-handler.ts.html +1132 -0
  64. package/coverage/lcov-report/lib/providers/walletconnect/core/index.html +176 -0
  65. package/coverage/lcov-report/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  66. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  67. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  68. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  69. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  70. package/coverage/lcov-report/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  71. package/coverage/lcov-report/lib/providers/walletconnect/signers/index.html +146 -0
  72. package/coverage/lcov-report/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  73. package/coverage/lcov-report/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  74. package/coverage/lcov-report/lib/services/index.html +191 -0
  75. package/coverage/lcov-report/lib/services/logger.service.ts.html +463 -0
  76. package/coverage/lcov-report/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  77. package/coverage/lcov-report/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  78. package/coverage/lcov-report/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  79. package/coverage/lcov-report/lib/services/transaction-builders/index.html +161 -0
  80. package/coverage/lcov-report/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  81. package/coverage/lcov-report/lib/services/transaction.service.ts.html +1123 -0
  82. package/coverage/lcov-report/lib/services/unified-wallet.service.ts.html +2641 -0
  83. package/coverage/lcov-report/lib/services/wallet-context.service.ts.html +637 -0
  84. package/coverage/lcov-report/lib/services/wallet-event-bus.service.ts.html +643 -0
  85. package/coverage/lcov-report/lib/services/wallet-providers.service.ts.html +496 -0
  86. package/coverage/lcov-report/lib/transports/chrome-extension-transport.ts.html +823 -0
  87. package/coverage/lcov-report/lib/transports/index.html +116 -0
  88. package/coverage/lcov-report/lib/utils/index.html +116 -0
  89. package/coverage/lcov-report/lib/utils/ledger-icons.util.ts.html +319 -0
  90. package/coverage/lcov-report/prettify.css +1 -0
  91. package/coverage/lcov-report/prettify.js +2 -0
  92. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  93. package/coverage/lcov-report/sorter.js +210 -0
  94. package/coverage/lcov.info +19252 -0
  95. package/coverage/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  96. package/coverage/lib/components/account-selector/account-actions/index.html +116 -0
  97. package/coverage/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  98. package/coverage/lib/components/account-selector/account-filter/index.html +116 -0
  99. package/coverage/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  100. package/coverage/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  101. package/coverage/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  102. package/coverage/lib/components/account-selector/account-list/index.html +116 -0
  103. package/coverage/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  104. package/coverage/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  105. package/coverage/lib/components/account-selector/index.html +161 -0
  106. package/coverage/lib/components/wallet-account-display/index.html +116 -0
  107. package/coverage/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  108. package/coverage/lib/components/wallet-connect-button/index.html +116 -0
  109. package/coverage/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  110. package/coverage/lib/components/wallet-connect-prompt/index.html +116 -0
  111. package/coverage/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  112. package/coverage/lib/components/wallet-connected-guard/index.html +116 -0
  113. package/coverage/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  114. package/coverage/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  115. package/coverage/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  116. package/coverage/lib/components/wallet-connection-modal/index.html +116 -0
  117. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  118. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  119. package/coverage/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  120. package/coverage/lib/components/wallet-session-display/index.html +116 -0
  121. package/coverage/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  122. package/coverage/lib/components/wallet-transaction-status/index.html +116 -0
  123. package/coverage/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  124. package/coverage/lib/directives/index.html +146 -0
  125. package/coverage/lib/directives/wallet-connected.directive.ts.html +670 -0
  126. package/coverage/lib/directives/wallet-context.directive.ts.html +547 -0
  127. package/coverage/lib/directives/wallet-events.directive.ts.html +781 -0
  128. package/coverage/lib/hsuite-wallet.module.ts.html +715 -0
  129. package/coverage/lib/index.html +116 -0
  130. package/coverage/lib/models/connection-config.model.ts.html +280 -0
  131. package/coverage/lib/models/index.html +131 -0
  132. package/coverage/lib/models/provider-types.ts.html +577 -0
  133. package/coverage/lib/providers/base-wallet-provider.ts.html +1138 -0
  134. package/coverage/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  135. package/coverage/lib/providers/hsuite-native/index.html +116 -0
  136. package/coverage/lib/providers/hsuite-native-provider.ts.html +2347 -0
  137. package/coverage/lib/providers/index.html +146 -0
  138. package/coverage/lib/providers/p2p-native/index.html +131 -0
  139. package/coverage/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  140. package/coverage/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  141. package/coverage/lib/providers/wallet-error-handler.ts.html +1132 -0
  142. package/coverage/lib/providers/walletconnect/core/index.html +176 -0
  143. package/coverage/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  144. package/coverage/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  145. package/coverage/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  146. package/coverage/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  147. package/coverage/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  148. package/coverage/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  149. package/coverage/lib/providers/walletconnect/signers/index.html +146 -0
  150. package/coverage/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  151. package/coverage/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  152. package/coverage/lib/services/index.html +191 -0
  153. package/coverage/lib/services/logger.service.ts.html +463 -0
  154. package/coverage/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  155. package/coverage/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  156. package/coverage/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  157. package/coverage/lib/services/transaction-builders/index.html +161 -0
  158. package/coverage/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  159. package/coverage/lib/services/transaction.service.ts.html +1123 -0
  160. package/coverage/lib/services/unified-wallet.service.ts.html +2641 -0
  161. package/coverage/lib/services/wallet-context.service.ts.html +637 -0
  162. package/coverage/lib/services/wallet-event-bus.service.ts.html +643 -0
  163. package/coverage/lib/services/wallet-providers.service.ts.html +496 -0
  164. package/coverage/lib/transports/chrome-extension-transport.ts.html +823 -0
  165. package/coverage/lib/transports/index.html +116 -0
  166. package/coverage/lib/utils/index.html +116 -0
  167. package/coverage/lib/utils/ledger-icons.util.ts.html +319 -0
  168. package/coverage/prettify.css +1 -0
  169. package/coverage/prettify.js +2 -0
  170. package/coverage/sort-arrow-sprite.png +0 -0
  171. package/coverage/sorter.js +210 -0
  172. package/dist/README.md +48 -0
  173. package/dist/fesm2022/hsuite-native-connect-angular.mjs +14592 -0
  174. package/dist/fesm2022/hsuite-native-connect-angular.mjs.map +1 -0
  175. package/dist/index.d.ts +6949 -0
  176. package/examples/minimal-connect.ts +178 -0
  177. package/examples/multi-protocol.ts +495 -0
  178. package/examples/transaction-signing.ts +361 -0
  179. package/jest.config.json +45 -0
  180. package/karma.conf.js +42 -0
  181. package/ng-package.json +20 -0
  182. package/package.json +60 -0
  183. package/src/index.ts +203 -0
  184. package/src/lib/components/account-selector/account-actions/account-actions.component.ts +261 -0
  185. package/src/lib/components/account-selector/account-filter/account-filter.component.ts +401 -0
  186. package/src/lib/components/account-selector/account-formatting.service.ts +200 -0
  187. package/src/lib/components/account-selector/account-grouping.service.ts +227 -0
  188. package/src/lib/components/account-selector/account-list/account-list.component.ts +470 -0
  189. package/src/lib/components/account-selector/account-selector.component.html +135 -0
  190. package/src/lib/components/account-selector/account-selector.component.scss +2039 -0
  191. package/src/lib/components/account-selector/account-selector.component.ts +470 -0
  192. package/src/lib/components/account-selector/account-selector.service.ts +501 -0
  193. package/src/lib/components/wallet-account-display/wallet-account-display.component.html +34 -0
  194. package/src/lib/components/wallet-account-display/wallet-account-display.component.scss +99 -0
  195. package/src/lib/components/wallet-account-display/wallet-account-display.component.ts +140 -0
  196. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.html +14 -0
  197. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.scss +272 -0
  198. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.ts +240 -0
  199. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.html +24 -0
  200. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.scss +50 -0
  201. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts +108 -0
  202. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.html +24 -0
  203. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts +73 -0
  204. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.html +56 -0
  205. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.scss +218 -0
  206. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts +117 -0
  207. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.html +94 -0
  208. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.scss +272 -0
  209. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts +734 -0
  210. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.html +197 -0
  211. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.scss +678 -0
  212. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts +730 -0
  213. package/src/lib/components/wallet-session-display/wallet-session-display.component.html +110 -0
  214. package/src/lib/components/wallet-session-display/wallet-session-display.component.scss +179 -0
  215. package/src/lib/components/wallet-session-display/wallet-session-display.component.ts +197 -0
  216. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.html +65 -0
  217. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.scss +254 -0
  218. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts +206 -0
  219. package/src/lib/directives/wallet-connected.directive.ts +195 -0
  220. package/src/lib/directives/wallet-context.directive.ts +154 -0
  221. package/src/lib/directives/wallet-events.directive.ts +232 -0
  222. package/src/lib/hsuite-wallet.module.ts +210 -0
  223. package/src/lib/models/connection-config.model.ts +65 -0
  224. package/src/lib/models/provider-types.ts +164 -0
  225. package/src/lib/models/unified-account.model.ts +76 -0
  226. package/src/lib/models/wallet-context.model.ts +121 -0
  227. package/src/lib/models/wallet-events.model.ts +158 -0
  228. package/src/lib/providers/base-wallet-provider.ts +351 -0
  229. package/src/lib/providers/hsuite-native/channel-client.service.spec.ts +73 -0
  230. package/src/lib/providers/hsuite-native/channel-client.service.ts +862 -0
  231. package/src/lib/providers/hsuite-native/index.ts +8 -0
  232. package/src/lib/providers/hsuite-native-provider.ts +754 -0
  233. package/src/lib/providers/mobile-native/mobile-native.provider.spec.ts +19 -0
  234. package/src/lib/providers/p2p-native/index.ts +30 -0
  235. package/src/lib/providers/p2p-native/p2p-native.provider.spec.ts +523 -0
  236. package/src/lib/providers/p2p-native/p2p-native.provider.ts +723 -0
  237. package/src/lib/providers/p2p-native/p2p-session-manager.ts +695 -0
  238. package/src/lib/providers/wallet-error-handler.ts +349 -0
  239. package/src/lib/providers/walletconnect/core/base-signer.interface.ts +122 -0
  240. package/src/lib/providers/walletconnect/core/session-health.ts +196 -0
  241. package/src/lib/providers/walletconnect/core/walletconnect-client-manager.ts +364 -0
  242. package/src/lib/providers/walletconnect/core/walletconnect-provider.integration.spec.ts +348 -0
  243. package/src/lib/providers/walletconnect/core/walletconnect-provider.ts +826 -0
  244. package/src/lib/providers/walletconnect/core/walletconnect-session-store.ts +273 -0
  245. package/src/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts +299 -0
  246. package/src/lib/providers/walletconnect/core/walletconnect-types.ts +48 -0
  247. package/src/lib/providers/walletconnect/index.ts +33 -0
  248. package/src/lib/providers/walletconnect/signers/hedera-signer.spec.ts +367 -0
  249. package/src/lib/providers/walletconnect/signers/hedera-signer.ts +610 -0
  250. package/src/lib/providers/walletconnect/signers/signer-factory.spec.ts +62 -0
  251. package/src/lib/providers/walletconnect/signers/signer-factory.ts +120 -0
  252. package/src/lib/providers/walletconnect/signers/xrpl-signer.spec.ts +296 -0
  253. package/src/lib/providers/walletconnect/signers/xrpl-signer.ts +478 -0
  254. package/src/lib/services/logger.service.ts +126 -0
  255. package/src/lib/services/transaction-builders/base-transaction-builder.service.ts +585 -0
  256. package/src/lib/services/transaction-builders/hedera-amount-utils.ts +84 -0
  257. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.spec.ts +741 -0
  258. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.ts +1285 -0
  259. package/src/lib/services/transaction-builders/index.ts +54 -0
  260. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.spec.ts +937 -0
  261. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.ts +832 -0
  262. package/src/lib/services/transaction.service.ts +346 -0
  263. package/src/lib/services/unified-wallet.service.spec.ts +1382 -0
  264. package/src/lib/services/unified-wallet.service.ts +852 -0
  265. package/src/lib/services/wallet-context.service.ts +184 -0
  266. package/src/lib/services/wallet-event-bus.service.ts +186 -0
  267. package/src/lib/services/wallet-providers.service.ts +137 -0
  268. package/src/lib/transports/chrome-extension-transport.ts +246 -0
  269. package/src/lib/utils/index.ts +14 -0
  270. package/src/lib/utils/ledger-icons.util.ts +78 -0
  271. package/test/test-setup.ts +21 -0
  272. package/test-setup.ts +63 -0
  273. package/tsconfig.build.json +11 -0
  274. package/tsconfig.json +29 -0
  275. package/tsconfig.spec.json +15 -0
  276. package/vitest.config.ts +48 -0
@@ -0,0 +1,734 @@
1
+ /**
2
+ * HSuite Native Connect
3
+ * Copyright 2024-2025 HSuite (https://hsuite.finance)
4
+ *
5
+ * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
6
+ *
7
+ * This file is part of HSuite Native Connect. For commercial licensing,
8
+ * visit https://hsuite.finance/licensing
9
+ */
10
+
11
+ /**
12
+ * @file qr-pairing-step.component.ts
13
+ * @description QR code pairing step for mobile wallet connection
14
+ *
15
+ * Implements single-QR Nostr-first pairing flow:
16
+ * 1. Display compact QR code with session invite
17
+ * 2. Wait for wallet to scan and connect via Nostr
18
+ * 3. Wait for user approval in wallet
19
+ * 4. Connection established (P2P upgrade happens automatically)
20
+ *
21
+ * This enables fully decentralized cross-device connections with only
22
+ * a single QR scan - much simpler than the previous 2-QR WebRTC flow.
23
+ *
24
+ * Features:
25
+ * - Single QR code display (no answer scanning needed)
26
+ * - QR code with HSuite logo in center
27
+ * - Countdown timer with expiry handling
28
+ * - Refresh capability for expired offers
29
+ * - Waiting for approval state
30
+ */
31
+
32
+ import { CommonModule } from '@angular/common';
33
+ import {
34
+ Component,
35
+ Input,
36
+ Output,
37
+ EventEmitter,
38
+ ChangeDetectionStrategy,
39
+ signal,
40
+ OnChanges,
41
+ SimpleChanges,
42
+ OnDestroy,
43
+ AfterViewInit,
44
+ ElementRef,
45
+ ViewChild,
46
+ inject,
47
+ } from '@angular/core';
48
+ import { copyToClipboard } from '@hsuite/native-connect-ui';
49
+ import { IonIcon, IonSpinner, IonButton } from '@ionic/angular/standalone';
50
+ import { addIcons } from 'ionicons';
51
+ import {
52
+ qrCodeOutline,
53
+ refreshOutline,
54
+ checkmarkCircleOutline,
55
+ closeCircleOutline,
56
+ phonePortraitOutline,
57
+ arrowBackOutline,
58
+ timeOutline,
59
+ copyOutline,
60
+ checkmarkOutline,
61
+ } from 'ionicons/icons';
62
+ import * as QRCode from 'qrcode'; // qrcode has no default export; use namespace import for toCanvas
63
+
64
+ import { LoggerService } from '../../../services/logger.service';
65
+
66
+ /**
67
+ * Connection state for the QR pairing step.
68
+ *
69
+ * States:
70
+ * - loading: Generating session invite
71
+ * - displaying: Showing QR code for wallet to scan
72
+ * - waiting_wallet: QR scanned, waiting for wallet to connect via Nostr
73
+ * - waiting_approval: Wallet connected, waiting for user to approve
74
+ * - connected: Session approved and connected
75
+ * - expired: QR code expired
76
+ * - error: An error occurred
77
+ */
78
+ export type QrPairingState =
79
+ | 'loading'
80
+ | 'displaying'
81
+ | 'waiting_wallet'
82
+ | 'waiting_approval'
83
+ | 'connected'
84
+ | 'expired'
85
+ | 'error';
86
+
87
+ /**
88
+ * QR Pairing Step Component
89
+ *
90
+ * Implements single-QR Nostr-first pairing for cross-device P2P connections.
91
+ * Much simpler than the previous 2-QR WebRTC approach.
92
+ */
93
+ @Component({
94
+ selector: 'hsuite-qr-pairing-step',
95
+ standalone: true,
96
+ changeDetection: ChangeDetectionStrategy.OnPush,
97
+ imports: [CommonModule, IonIcon, IonSpinner, IonButton],
98
+ template: `
99
+ <div class="qr-pairing-container">
100
+ <!-- Hidden canvas for QR generation - always rendered -->
101
+ <canvas #qrCanvas width="300" height="300" class="hidden-canvas"></canvas>
102
+
103
+ <!-- Loading State -->
104
+ <div *ngIf="pairingState() === 'loading'" class="state-panel loading">
105
+ <ion-spinner name="crescent"></ion-spinner>
106
+ <p>Generating secure connection...</p>
107
+ </div>
108
+
109
+ <!-- QR Display State -->
110
+ <div *ngIf="pairingState() === 'displaying'" class="qr-display">
111
+ <div class="qr-header">
112
+ <h3>Scan with Your Wallet</h3>
113
+ <p class="qr-subtitle">Open HSuite Wallet and scan this QR code</p>
114
+ </div>
115
+
116
+ <div class="qr-wrapper">
117
+ <canvas #qrCanvasDisplay width="300" height="300"></canvas>
118
+ </div>
119
+
120
+ <div class="qr-meta">
121
+ <div class="countdown" [class.warning]="countdown() < 30">
122
+ <ion-icon name="time-outline"></ion-icon>
123
+ <span>{{ formatCountdown() }}</span>
124
+ </div>
125
+ <ion-button fill="clear" size="small" (click)="copyUri()">
126
+ <ion-icon
127
+ slot="icon-only"
128
+ [name]="copied() ? 'checkmark-outline' : 'copy-outline'"
129
+ ></ion-icon>
130
+ </ion-button>
131
+ </div>
132
+
133
+ <div class="instructions">
134
+ <ol>
135
+ <li>Open HSuite Wallet on your phone</li>
136
+ <li>Go to Sessions tab</li>
137
+ <li>Tap "Scan QR" and scan this code</li>
138
+ </ol>
139
+ </div>
140
+ </div>
141
+
142
+ <!-- Waiting for Wallet State -->
143
+ <div *ngIf="pairingState() === 'waiting_wallet'" class="state-panel waiting">
144
+ <div class="waiting-icon-wrapper">
145
+ <ion-spinner name="crescent"></ion-spinner>
146
+ </div>
147
+ <h3>Waiting for Wallet</h3>
148
+ <p class="waiting-description">Scan the QR code with your wallet app to connect.</p>
149
+ </div>
150
+
151
+ <!-- Waiting for Approval State -->
152
+ <div *ngIf="pairingState() === 'waiting_approval'" class="state-panel waiting-approval">
153
+ <div class="waiting-icon-wrapper">
154
+ <ion-spinner name="crescent"></ion-spinner>
155
+ </div>
156
+ <h3>Waiting for Wallet Approval</h3>
157
+ <p class="waiting-description">
158
+ The wallet is connected. Please approve the session in your wallet app.
159
+ </p>
160
+ <div class="waiting-steps">
161
+ <div class="waiting-step">
162
+ <span class="step-check">&#10003;</span>
163
+ <span>QR code scanned</span>
164
+ </div>
165
+ <div class="waiting-step">
166
+ <span class="step-check">&#10003;</span>
167
+ <span>Wallet connected</span>
168
+ </div>
169
+ <div class="waiting-step active">
170
+ <ion-spinner name="dots"></ion-spinner>
171
+ <span>Awaiting approval in wallet...</span>
172
+ </div>
173
+ </div>
174
+ </div>
175
+
176
+ <!-- Connected State -->
177
+ <div *ngIf="pairingState() === 'connected'" class="state-panel connected">
178
+ <ion-icon name="checkmark-circle-outline" color="success"></ion-icon>
179
+ <p>Connected successfully!</p>
180
+ </div>
181
+
182
+ <!-- Error State -->
183
+ <div *ngIf="pairingState() === 'error'" class="state-panel error">
184
+ <ion-icon name="close-circle-outline" color="danger"></ion-icon>
185
+ <p>{{ errorMessage || 'Connection failed' }}</p>
186
+ <ion-button fill="outline" (click)="refresh()">
187
+ <ion-icon slot="start" name="refresh-outline"></ion-icon>
188
+ Try Again
189
+ </ion-button>
190
+ </div>
191
+
192
+ <!-- Expired State -->
193
+ <div *ngIf="pairingState() === 'expired'" class="state-panel expired">
194
+ <ion-icon name="time-outline" color="warning"></ion-icon>
195
+ <p>QR code expired</p>
196
+ <ion-button fill="outline" (click)="refresh()">
197
+ <ion-icon slot="start" name="refresh-outline"></ion-icon>
198
+ Generate New
199
+ </ion-button>
200
+ </div>
201
+
202
+ <!-- Footer Actions (cancel) - hidden during waiting/connected -->
203
+ <div
204
+ class="footer-actions"
205
+ *ngIf="!['waiting_wallet', 'waiting_approval', 'connected'].includes(pairingState())"
206
+ >
207
+ <ion-button fill="clear" (click)="cancel()">
208
+ <ion-icon slot="start" name="arrow-back-outline"></ion-icon>
209
+ Cancel
210
+ </ion-button>
211
+ </div>
212
+ </div>
213
+ `,
214
+ styles: [
215
+ `
216
+ .qr-pairing-container {
217
+ display: flex;
218
+ flex-direction: column;
219
+ padding: 1rem;
220
+ min-height: 400px;
221
+ }
222
+
223
+ .hidden-canvas {
224
+ position: absolute;
225
+ left: -9999px;
226
+ top: -9999px;
227
+ visibility: hidden;
228
+ }
229
+
230
+ .qr-display {
231
+ display: flex;
232
+ flex-direction: column;
233
+ align-items: center;
234
+ flex: 1;
235
+ }
236
+
237
+ .qr-header {
238
+ text-align: center;
239
+ margin-bottom: 1.5rem;
240
+ }
241
+
242
+ .qr-header h3 {
243
+ margin: 0 0 0.5rem 0;
244
+ font-size: 1.25rem;
245
+ font-weight: 600;
246
+ }
247
+
248
+ .qr-subtitle {
249
+ margin: 0;
250
+ color: var(--ion-color-medium-shade);
251
+ font-size: 0.875rem;
252
+ }
253
+
254
+ .qr-wrapper {
255
+ display: flex;
256
+ justify-content: center;
257
+ padding: 1rem;
258
+ background: white;
259
+ border-radius: 12px;
260
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
261
+ margin-bottom: 1rem;
262
+ }
263
+
264
+ .qr-meta {
265
+ display: flex;
266
+ justify-content: center;
267
+ align-items: center;
268
+ gap: 1rem;
269
+ margin-bottom: 1rem;
270
+ }
271
+
272
+ .countdown {
273
+ display: flex;
274
+ align-items: center;
275
+ gap: 0.25rem;
276
+ padding: 0.25rem 0.75rem;
277
+ background: var(--ion-color-light);
278
+ border-radius: 1rem;
279
+ font-size: 0.875rem;
280
+ }
281
+
282
+ .countdown.warning {
283
+ background: var(--ion-color-warning-tint);
284
+ color: var(--ion-color-warning-shade);
285
+ }
286
+
287
+ .instructions {
288
+ margin-bottom: 1rem;
289
+ width: 100%;
290
+ max-width: 300px;
291
+ }
292
+
293
+ .instructions ol {
294
+ text-align: left;
295
+ padding-left: 1.25rem;
296
+ margin: 0;
297
+ }
298
+
299
+ .instructions li {
300
+ margin-bottom: 0.5rem;
301
+ color: var(--ion-color-medium-shade);
302
+ font-size: 0.875rem;
303
+ }
304
+
305
+ .state-panel {
306
+ display: flex;
307
+ flex-direction: column;
308
+ align-items: center;
309
+ justify-content: center;
310
+ gap: 1rem;
311
+ flex: 1;
312
+ text-align: center;
313
+ }
314
+
315
+ .state-panel ion-spinner {
316
+ width: 48px;
317
+ height: 48px;
318
+ }
319
+
320
+ .state-panel ion-icon {
321
+ font-size: 64px;
322
+ }
323
+
324
+ /* Waiting States */
325
+ .waiting,
326
+ .waiting-approval {
327
+ padding: 1.5rem;
328
+ }
329
+
330
+ .waiting h3,
331
+ .waiting-approval h3 {
332
+ margin: 0 0 0.5rem 0;
333
+ font-size: 1.25rem;
334
+ font-weight: 600;
335
+ color: var(--ion-color-primary);
336
+ }
337
+
338
+ .waiting-description {
339
+ color: var(--ion-color-medium-shade);
340
+ font-size: 0.9rem;
341
+ margin-bottom: 1.5rem;
342
+ max-width: 280px;
343
+ }
344
+
345
+ .waiting-icon-wrapper {
346
+ width: 80px;
347
+ height: 80px;
348
+ border-radius: 50%;
349
+ background: var(--ion-color-primary-tint);
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: center;
353
+ margin-bottom: 1rem;
354
+ }
355
+
356
+ .waiting-icon-wrapper ion-spinner {
357
+ width: 40px;
358
+ height: 40px;
359
+ --color: var(--ion-color-primary);
360
+ }
361
+
362
+ .waiting-steps {
363
+ display: flex;
364
+ flex-direction: column;
365
+ gap: 0.75rem;
366
+ padding: 1rem;
367
+ background: var(--ion-color-light);
368
+ border-radius: 12px;
369
+ width: 100%;
370
+ max-width: 280px;
371
+ }
372
+
373
+ .waiting-step {
374
+ display: flex;
375
+ align-items: center;
376
+ gap: 0.75rem;
377
+ font-size: 0.875rem;
378
+ color: var(--ion-color-medium-shade);
379
+ }
380
+
381
+ .waiting-step .step-check {
382
+ width: 20px;
383
+ height: 20px;
384
+ border-radius: 50%;
385
+ background: var(--ion-color-success);
386
+ color: white;
387
+ font-size: 0.75rem;
388
+ display: flex;
389
+ align-items: center;
390
+ justify-content: center;
391
+ flex-shrink: 0;
392
+ }
393
+
394
+ .waiting-step.active {
395
+ color: var(--ion-color-primary);
396
+ font-weight: 500;
397
+ }
398
+
399
+ .waiting-step.active ion-spinner {
400
+ width: 20px;
401
+ height: 20px;
402
+ --color: var(--ion-color-primary);
403
+ }
404
+
405
+ .footer-actions {
406
+ margin-top: auto;
407
+ padding-top: 1rem;
408
+ border-top: 1px solid var(--ion-color-light);
409
+ display: flex;
410
+ justify-content: center;
411
+ }
412
+ `,
413
+ ],
414
+ })
415
+ export class QrPairingStepComponent implements AfterViewInit, OnChanges, OnDestroy {
416
+ private readonly logger = inject(LoggerService).scoped('QrPairingStep');
417
+
418
+ /**
419
+ * The encoded offer data to display as QR code (hsc:connect?i=...)
420
+ */
421
+ @Input() offerData: string = '';
422
+
423
+ /**
424
+ * App name to display in instructions
425
+ */
426
+ @Input() appName: string = 'This app';
427
+
428
+ /**
429
+ * Whether currently loading/generating the offer
430
+ */
431
+ @Input() isLoading: boolean = false;
432
+
433
+ /**
434
+ * Whether waiting for wallet to connect via Nostr
435
+ */
436
+ @Input() isWaitingForWallet: boolean = false;
437
+
438
+ /**
439
+ * Whether waiting for wallet user to approve the session
440
+ */
441
+ @Input() isWaitingForApproval: boolean = false;
442
+
443
+ /**
444
+ * Error message if any
445
+ */
446
+ @Input() errorMessage: string | null = null;
447
+
448
+ /**
449
+ * Expiry time in seconds for the QR code
450
+ */
451
+ @Input() expirySeconds: number = 120;
452
+
453
+ /**
454
+ * Emitted when QR code expires
455
+ */
456
+ @Output() expired = new EventEmitter<void>();
457
+
458
+ /**
459
+ * Emitted when connection is established
460
+ */
461
+ @Output() connected = new EventEmitter<void>();
462
+
463
+ /**
464
+ * Emitted when user cancels/goes back
465
+ */
466
+ @Output() cancelled = new EventEmitter<void>();
467
+
468
+ /**
469
+ * Emitted when user requests refresh
470
+ */
471
+ @Output() refreshRequested = new EventEmitter<void>();
472
+
473
+ @ViewChild('qrCanvas') qrCanvas?: ElementRef<HTMLCanvasElement>;
474
+ @ViewChild('qrCanvasDisplay') qrCanvasDisplay?: ElementRef<HTMLCanvasElement>;
475
+
476
+ // Internal state
477
+ readonly pairingState = signal<QrPairingState>('loading');
478
+ readonly countdown = signal<number>(120);
479
+ readonly copied = signal<boolean>(false);
480
+
481
+ private countdownInterval?: ReturnType<typeof setInterval>;
482
+ private viewReady = false;
483
+ private pendingQrGeneration = false;
484
+
485
+ /**
486
+ *
487
+ */
488
+ constructor() {
489
+ addIcons({
490
+ qrCodeOutline,
491
+ refreshOutline,
492
+ checkmarkCircleOutline,
493
+ closeCircleOutline,
494
+ phonePortraitOutline,
495
+ arrowBackOutline,
496
+ timeOutline,
497
+ copyOutline,
498
+ checkmarkOutline,
499
+ });
500
+ }
501
+
502
+ /**
503
+ *
504
+ */
505
+ ngAfterViewInit(): void {
506
+ this.viewReady = true;
507
+ // Generate QR if we have pending data
508
+ if (this.pendingQrGeneration && this.offerData) {
509
+ void this.generateQRCode(this.offerData);
510
+ }
511
+ }
512
+
513
+ /**
514
+ *
515
+ * @param changes
516
+ */
517
+ ngOnChanges(changes: SimpleChanges): void {
518
+ // Update state based on inputs
519
+ if (
520
+ changes['isLoading'] ||
521
+ changes['isWaitingForWallet'] ||
522
+ changes['isWaitingForApproval'] ||
523
+ changes['offerData'] ||
524
+ changes['errorMessage']
525
+ ) {
526
+ this.updateState();
527
+ }
528
+
529
+ if (changes['offerData'] && this.offerData) {
530
+ if (this.viewReady) {
531
+ void this.generateQRCode(this.offerData);
532
+ } else {
533
+ this.pendingQrGeneration = true;
534
+ }
535
+ }
536
+
537
+ if (changes['expirySeconds']) {
538
+ this.countdown.set(this.expirySeconds);
539
+ }
540
+ }
541
+
542
+ /**
543
+ *
544
+ */
545
+ ngOnDestroy(): void {
546
+ this.stopCountdown();
547
+ }
548
+
549
+ /**
550
+ * Format countdown for display (MM:SS)
551
+ */
552
+ formatCountdown(): string {
553
+ const seconds = this.countdown();
554
+ const mins = Math.floor(seconds / 60);
555
+ const secs = seconds % 60;
556
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
557
+ }
558
+
559
+ /**
560
+ * Handle QR code expiry
561
+ */
562
+ onQrExpired(): void {
563
+ this.pairingState.set('expired');
564
+ this.expired.emit();
565
+ }
566
+
567
+ /**
568
+ * Request a new QR code
569
+ */
570
+ refresh(): void {
571
+ this.pairingState.set('loading');
572
+ this.refreshRequested.emit();
573
+ }
574
+
575
+ /**
576
+ * Go back / cancel
577
+ */
578
+ cancel(): void {
579
+ this.cancelled.emit();
580
+ }
581
+
582
+ /**
583
+ * Copy the offer URI to clipboard
584
+ */
585
+ async copyUri(): Promise<void> {
586
+ if (!this.offerData) return;
587
+
588
+ const success = await copyToClipboard(this.offerData);
589
+
590
+ if (success) {
591
+ this.copied.set(true);
592
+
593
+ // Reset after 2 seconds
594
+ setTimeout(() => {
595
+ this.copied.set(false);
596
+ }, 2000);
597
+ } else {
598
+ this.logger.error('Failed to copy URI');
599
+ }
600
+ }
601
+
602
+ // ========== Private Methods ==========
603
+
604
+ /**
605
+ * Generate QR code with HSuite logo overlay
606
+ * @param data
607
+ */
608
+ private async generateQRCode(data: string): Promise<void> {
609
+ const canvas = this.qrCanvas?.nativeElement;
610
+ if (!canvas) {
611
+ this.logger.warn('Canvas not available for QR generation');
612
+ return;
613
+ }
614
+
615
+ try {
616
+ // Generate QR code - session invites are small (~300 bytes), so we can use higher error correction
617
+ await QRCode.toCanvas(canvas, data, {
618
+ width: 300,
619
+ margin: 2,
620
+ color: {
621
+ dark: '#000000',
622
+ light: '#ffffff',
623
+ },
624
+ errorCorrectionLevel: 'M', // Medium error correction
625
+ });
626
+
627
+ // Add HSuite logo in center
628
+ const ctx = canvas.getContext('2d');
629
+ if (!ctx) return;
630
+
631
+ // Load logo
632
+ const logo = new Image();
633
+ logo.crossOrigin = 'anonymous';
634
+ logo.onload = () => {
635
+ // Logo size is 20% of QR code
636
+ const logoSize = Math.floor(canvas.width * 0.2);
637
+ const logoX = (canvas.width - logoSize) / 2;
638
+ const logoY = (canvas.height - logoSize) / 2;
639
+
640
+ // Draw white background circle for logo
641
+ const circlePadding = 4;
642
+ ctx.fillStyle = '#ffffff';
643
+ ctx.beginPath();
644
+ ctx.arc(canvas.width / 2, canvas.height / 2, logoSize / 2 + circlePadding, 0, 2 * Math.PI);
645
+ ctx.fill();
646
+
647
+ // Draw logo
648
+ ctx.drawImage(logo, logoX, logoY, logoSize, logoSize);
649
+
650
+ // Show the QR display and copy to visible canvas
651
+ this.pairingState.set('displaying');
652
+ this.startCountdown();
653
+
654
+ // Copy to display canvas after a tick
655
+ setTimeout(() => this.copyToDisplayCanvas(), 0);
656
+ };
657
+ logo.onerror = () => {
658
+ // Still show QR even without logo
659
+ this.pairingState.set('displaying');
660
+ this.startCountdown();
661
+ setTimeout(() => this.copyToDisplayCanvas(), 0);
662
+ };
663
+ logo.src = '/assets/hsuite_icon.png';
664
+ } catch (error) {
665
+ this.logger.error('Failed to generate QR code', {
666
+ error: error instanceof Error ? error.message : String(error),
667
+ });
668
+ this.pairingState.set('error');
669
+ }
670
+ }
671
+
672
+ /**
673
+ * Copy the generated QR from hidden canvas to display canvas
674
+ */
675
+ private copyToDisplayCanvas(): void {
676
+ const srcCanvas = this.qrCanvas?.nativeElement;
677
+ const destCanvas = this.qrCanvasDisplay?.nativeElement;
678
+
679
+ if (!srcCanvas || !destCanvas) {
680
+ this.logger.warn('Cannot copy to display canvas - missing canvas elements');
681
+ return;
682
+ }
683
+
684
+ const destCtx = destCanvas.getContext('2d');
685
+ if (!destCtx) return;
686
+
687
+ destCtx.drawImage(srcCanvas, 0, 0);
688
+ }
689
+
690
+ /**
691
+ * Start countdown timer
692
+ */
693
+ private startCountdown(): void {
694
+ this.stopCountdown();
695
+ this.countdown.set(this.expirySeconds);
696
+
697
+ this.countdownInterval = setInterval(() => {
698
+ const current = this.countdown();
699
+ if (current <= 1) {
700
+ this.stopCountdown();
701
+ this.onQrExpired();
702
+ } else {
703
+ this.countdown.set(current - 1);
704
+ }
705
+ }, 1000);
706
+ }
707
+
708
+ /**
709
+ * Stop countdown timer
710
+ */
711
+ private stopCountdown(): void {
712
+ if (this.countdownInterval) {
713
+ clearInterval(this.countdownInterval);
714
+ this.countdownInterval = undefined;
715
+ }
716
+ }
717
+
718
+ /**
719
+ * Update internal state based on inputs.
720
+ * Priority: error > waiting_approval > waiting_wallet > loading
721
+ */
722
+ private updateState(): void {
723
+ if (this.errorMessage) {
724
+ this.pairingState.set('error');
725
+ } else if (this.isWaitingForApproval) {
726
+ this.pairingState.set('waiting_approval');
727
+ } else if (this.isWaitingForWallet) {
728
+ this.pairingState.set('waiting_wallet');
729
+ } else if (this.isLoading && !this.offerData) {
730
+ this.pairingState.set('loading');
731
+ }
732
+ // Note: 'displaying' is set after QR generation completes
733
+ }
734
+ }