@lightsparkdev/lightspark-sdk 0.1.6

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 (223) hide show
  1. package/.fossa.yml +6 -0
  2. package/.prettierrc +1 -0
  3. package/.turbo/turbo-build.log +19 -0
  4. package/.turbo/turbo-lint.log +3 -0
  5. package/CHANGELOG.md +49 -0
  6. package/LICENSE +201 -0
  7. package/README.md +137 -0
  8. package/dist/Withdrawal-17e1c8af.d.ts +1672 -0
  9. package/dist/Withdrawal-27a4d10d.d.ts +1672 -0
  10. package/dist/chunk-3VRI7CHE.js +5508 -0
  11. package/dist/chunk-AGEUDR2V.js +4498 -0
  12. package/dist/chunk-N27QHRE4.js +5508 -0
  13. package/dist/client-3bba3f64.d.ts +1302 -0
  14. package/dist/index.cjs +6633 -0
  15. package/dist/index.d.ts +15 -0
  16. package/dist/index.js +1177 -0
  17. package/dist/objects/index.cjs +5347 -0
  18. package/dist/objects/index.d.ts +3 -0
  19. package/dist/objects/index.js +88 -0
  20. package/examples/node-scripts/authHelpers.ts +20 -0
  21. package/examples/node-scripts/createInvoice.ts +64 -0
  22. package/examples/node-scripts/example.ts +288 -0
  23. package/examples/node-scripts/getAccountDashboard.ts +24 -0
  24. package/examples/node-scripts/getNodeChannels.ts +34 -0
  25. package/examples/node-scripts/internalAuthHelpers.ts +26 -0
  26. package/examples/node-scripts/internal_example.ts +296 -0
  27. package/examples/node-scripts/package-lock.json +887 -0
  28. package/examples/node-scripts/package.json +22 -0
  29. package/examples/node-scripts/prettyPrintJsonForDocs.ts +62 -0
  30. package/examples/node-scripts/tsconfig.json +27 -0
  31. package/examples/oauth-example/README.md +19 -0
  32. package/examples/oauth-example/package-lock.json +16071 -0
  33. package/examples/oauth-example/package.json +50 -0
  34. package/examples/oauth-example/public/favicon.ico +0 -0
  35. package/examples/oauth-example/public/index.html +43 -0
  36. package/examples/oauth-example/public/logo192.png +0 -0
  37. package/examples/oauth-example/public/logo512.png +0 -0
  38. package/examples/oauth-example/public/manifest.json +25 -0
  39. package/examples/oauth-example/public/robots.txt +3 -0
  40. package/examples/oauth-example/src/App.css +7 -0
  41. package/examples/oauth-example/src/App.test.tsx +12 -0
  42. package/examples/oauth-example/src/App.tsx +16 -0
  43. package/examples/oauth-example/src/auth/AuthContext.ts +8 -0
  44. package/examples/oauth-example/src/auth/AuthProvider.tsx +44 -0
  45. package/examples/oauth-example/src/auth/RequireAuth.tsx +19 -0
  46. package/examples/oauth-example/src/auth/oauthProvider.ts +35 -0
  47. package/examples/oauth-example/src/components/Button.tsx +39 -0
  48. package/examples/oauth-example/src/components/CurrencyAmount.tsx +117 -0
  49. package/examples/oauth-example/src/components/Dashboard.tsx +158 -0
  50. package/examples/oauth-example/src/components/Table.tsx +22 -0
  51. package/examples/oauth-example/src/hooks/useAccountInfo.tsx +31 -0
  52. package/examples/oauth-example/src/icons/BitcoinB.tsx +20 -0
  53. package/examples/oauth-example/src/icons/Icon.tsx +121 -0
  54. package/examples/oauth-example/src/icons/Satoshi.tsx +28 -0
  55. package/examples/oauth-example/src/index.css +13 -0
  56. package/examples/oauth-example/src/index.tsx +23 -0
  57. package/examples/oauth-example/src/lightsparkclient/LightsparkClientContext.ts +10 -0
  58. package/examples/oauth-example/src/lightsparkclient/LightsparkClientProvider.tsx +53 -0
  59. package/examples/oauth-example/src/logo.svg +1 -0
  60. package/examples/oauth-example/src/pages/DashboardPage.tsx +71 -0
  61. package/examples/oauth-example/src/pages/LoginPage.tsx +63 -0
  62. package/examples/oauth-example/src/react-app-env.d.ts +1 -0
  63. package/examples/oauth-example/src/reportWebVitals.ts +15 -0
  64. package/examples/oauth-example/src/routes/index.tsx +15 -0
  65. package/examples/oauth-example/src/setupTests.ts +5 -0
  66. package/examples/oauth-example/src/utils/currency.ts +483 -0
  67. package/examples/oauth-example/tsconfig.json +20 -0
  68. package/examples/streaming-wallet-extension/.fossa.yml +6 -0
  69. package/examples/streaming-wallet-extension/README.md +17 -0
  70. package/examples/streaming-wallet-extension/craco.config.js +58 -0
  71. package/examples/streaming-wallet-extension/package-lock.json +18260 -0
  72. package/examples/streaming-wallet-extension/package.json +77 -0
  73. package/examples/streaming-wallet-extension/public/index.html +24 -0
  74. package/examples/streaming-wallet-extension/public/lightspark_full.png +0 -0
  75. package/examples/streaming-wallet-extension/public/lightspark_icon_circle.png +0 -0
  76. package/examples/streaming-wallet-extension/public/manifest.json +43 -0
  77. package/examples/streaming-wallet-extension/public/robots.txt +3 -0
  78. package/examples/streaming-wallet-extension/src/App.css +53 -0
  79. package/examples/streaming-wallet-extension/src/App.tsx +425 -0
  80. package/examples/streaming-wallet-extension/src/auth/AccountStorage.ts +28 -0
  81. package/examples/streaming-wallet-extension/src/auth/DemoAccountProvider.ts +99 -0
  82. package/examples/streaming-wallet-extension/src/auth/StreamingDemoCredentials.ts +10 -0
  83. package/examples/streaming-wallet-extension/src/background/PaymentStrategy.ts +36 -0
  84. package/examples/streaming-wallet-extension/src/background/PlaybackRange.ts +31 -0
  85. package/examples/streaming-wallet-extension/src/background/StreamingInvoiceHolder.ts +33 -0
  86. package/examples/streaming-wallet-extension/src/background/TransactionObserver.ts +66 -0
  87. package/examples/streaming-wallet-extension/src/background/VideoPlaybackRanges.ts +38 -0
  88. package/examples/streaming-wallet-extension/src/background/VideoProgressCache.ts +87 -0
  89. package/examples/streaming-wallet-extension/src/background/background.ts +145 -0
  90. package/examples/streaming-wallet-extension/src/background/messageHandling.ts +185 -0
  91. package/examples/streaming-wallet-extension/src/common/datetimes.ts +28 -0
  92. package/examples/streaming-wallet-extension/src/common/settings.ts +12 -0
  93. package/examples/streaming-wallet-extension/src/common/storage.ts +8 -0
  94. package/examples/streaming-wallet-extension/src/common/streamingTabs.ts +27 -0
  95. package/examples/streaming-wallet-extension/src/common/types.tsx +23 -0
  96. package/examples/streaming-wallet-extension/src/components/CirclePlusIcon.tsx +19 -0
  97. package/examples/streaming-wallet-extension/src/components/CurrencyAmount.tsx +110 -0
  98. package/examples/streaming-wallet-extension/src/components/CurrencyAmountRaw.tsx +195 -0
  99. package/examples/streaming-wallet-extension/src/components/LeftArrow.tsx +21 -0
  100. package/examples/streaming-wallet-extension/src/components/Loading.tsx +151 -0
  101. package/examples/streaming-wallet-extension/src/components/StreamingTransactionChip.tsx +95 -0
  102. package/examples/streaming-wallet-extension/src/components/TransactionRow.tsx +93 -0
  103. package/examples/streaming-wallet-extension/src/contentscript/content.ts +123 -0
  104. package/examples/streaming-wallet-extension/src/contentscript/lightsparkDemoDom.tsx +113 -0
  105. package/examples/streaming-wallet-extension/src/contentscript/videoElementParsers.ts +92 -0
  106. package/examples/streaming-wallet-extension/src/index.css +16 -0
  107. package/examples/streaming-wallet-extension/src/index.tsx +11 -0
  108. package/examples/streaming-wallet-extension/src/lightsparkClientProvider.tsx +26 -0
  109. package/examples/streaming-wallet-extension/src/react-app-env.d.ts +1 -0
  110. package/examples/streaming-wallet-extension/src/types/Messages.ts +17 -0
  111. package/examples/streaming-wallet-extension/tsconfig.json +20 -0
  112. package/package.json +87 -0
  113. package/src/auth/AccountTokenAuthProvider.ts +37 -0
  114. package/src/auth/index.ts +3 -0
  115. package/src/client.ts +759 -0
  116. package/src/graphql/BitcoinFeeEstimate.ts +13 -0
  117. package/src/graphql/CreateApiToken.ts +22 -0
  118. package/src/graphql/CreateInvoice.ts +18 -0
  119. package/src/graphql/CreateNodeWalletAddress.ts +13 -0
  120. package/src/graphql/CurrentAccount.ts +13 -0
  121. package/src/graphql/DecodeInvoice.ts +16 -0
  122. package/src/graphql/DeleteApiToken.ts +13 -0
  123. package/src/graphql/FundNode.ts +18 -0
  124. package/src/graphql/LightningFeeEstimateForInvoice.ts +21 -0
  125. package/src/graphql/LightningFeeEstimateForNode.ts +21 -0
  126. package/src/graphql/MultiNodeDashboard.ts +118 -0
  127. package/src/graphql/PayInvoice.ts +29 -0
  128. package/src/graphql/RecoverNodeSigningKey.ts +15 -0
  129. package/src/graphql/RequestWithdrawal.ts +25 -0
  130. package/src/graphql/SendPayment.ts +29 -0
  131. package/src/graphql/SingleNodeDashboard.ts +116 -0
  132. package/src/graphql/TransactionSubscription.ts +16 -0
  133. package/src/graphql/TransactionsForNode.ts +42 -0
  134. package/src/index.ts +5 -0
  135. package/src/objects/Account.ts +1222 -0
  136. package/src/objects/AccountToApiTokensConnection.ts +50 -0
  137. package/src/objects/AccountToChannelsConnection.ts +35 -0
  138. package/src/objects/AccountToNodesConnection.ts +62 -0
  139. package/src/objects/AccountToPaymentRequestsConnection.ts +50 -0
  140. package/src/objects/AccountToTransactionsConnection.ts +112 -0
  141. package/src/objects/ApiToken.ts +80 -0
  142. package/src/objects/BitcoinNetwork.ts +19 -0
  143. package/src/objects/BlockchainBalance.ts +102 -0
  144. package/src/objects/Channel.ts +283 -0
  145. package/src/objects/ChannelClosingTransaction.ts +150 -0
  146. package/src/objects/ChannelFees.ts +34 -0
  147. package/src/objects/ChannelOpeningTransaction.ts +150 -0
  148. package/src/objects/ChannelStatus.ts +25 -0
  149. package/src/objects/ChannelToTransactionsConnection.ts +86 -0
  150. package/src/objects/CreateApiTokenInput.ts +22 -0
  151. package/src/objects/CreateApiTokenOutput.ts +41 -0
  152. package/src/objects/CreateInvoiceInput.ts +27 -0
  153. package/src/objects/CreateInvoiceOutput.ts +21 -0
  154. package/src/objects/CreateNodeWalletAddressInput.ts +15 -0
  155. package/src/objects/CreateNodeWalletAddressOutput.ts +27 -0
  156. package/src/objects/CurrencyAmount.ts +55 -0
  157. package/src/objects/CurrencyUnit.ts +25 -0
  158. package/src/objects/DeleteApiTokenInput.ts +13 -0
  159. package/src/objects/DeleteApiTokenOutput.ts +23 -0
  160. package/src/objects/Deposit.ts +144 -0
  161. package/src/objects/Entity.ts +868 -0
  162. package/src/objects/FeeEstimate.ts +39 -0
  163. package/src/objects/FundNodeInput.ts +16 -0
  164. package/src/objects/FundNodeOutput.ts +28 -0
  165. package/src/objects/GraphNode.ts +110 -0
  166. package/src/objects/Hop.ts +108 -0
  167. package/src/objects/HtlcAttemptFailureCode.ts +65 -0
  168. package/src/objects/IncomingPayment.ts +141 -0
  169. package/src/objects/IncomingPaymentAttempt.ts +96 -0
  170. package/src/objects/IncomingPaymentAttemptStatus.ts +20 -0
  171. package/src/objects/IncomingPaymentToAttemptsConnection.ts +39 -0
  172. package/src/objects/Invoice.ts +226 -0
  173. package/src/objects/InvoiceData.ts +185 -0
  174. package/src/objects/InvoiceType.ts +15 -0
  175. package/src/objects/LightningFeeEstimateForInvoiceInput.ts +28 -0
  176. package/src/objects/LightningFeeEstimateForNodeInput.ts +25 -0
  177. package/src/objects/LightningFeeEstimateOutput.ts +33 -0
  178. package/src/objects/LightningTransaction.ts +393 -0
  179. package/src/objects/LightsparkNode.ts +377 -0
  180. package/src/objects/LightsparkNodePurpose.ts +17 -0
  181. package/src/objects/LightsparkNodeStatus.ts +29 -0
  182. package/src/objects/LightsparkNodeToChannelsConnection.ts +50 -0
  183. package/src/objects/Node.ts +273 -0
  184. package/src/objects/NodeAddress.ts +29 -0
  185. package/src/objects/NodeAddressType.ts +18 -0
  186. package/src/objects/NodeToAddressesConnection.ts +39 -0
  187. package/src/objects/OnChainTransaction.ts +318 -0
  188. package/src/objects/OutgoingPayment.ts +319 -0
  189. package/src/objects/OutgoingPaymentAttempt.ts +164 -0
  190. package/src/objects/OutgoingPaymentAttemptStatus.ts +18 -0
  191. package/src/objects/OutgoingPaymentAttemptToHopsConnection.ts +37 -0
  192. package/src/objects/OutgoingPaymentToAttemptsConnection.ts +39 -0
  193. package/src/objects/PageInfo.ts +31 -0
  194. package/src/objects/PayInvoiceInput.ts +33 -0
  195. package/src/objects/PayInvoiceOutput.ts +22 -0
  196. package/src/objects/PaymentFailureReason.ts +29 -0
  197. package/src/objects/PaymentRequest.ts +231 -0
  198. package/src/objects/PaymentRequestData.ts +183 -0
  199. package/src/objects/PaymentRequestStatus.ts +15 -0
  200. package/src/objects/Permission.ts +39 -0
  201. package/src/objects/RequestWithdrawalInput.ts +35 -0
  202. package/src/objects/RequestWithdrawalOutput.ts +24 -0
  203. package/src/objects/RichText.ts +19 -0
  204. package/src/objects/RoutingTransaction.ts +150 -0
  205. package/src/objects/RoutingTransactionFailureReason.ts +17 -0
  206. package/src/objects/Secret.ts +23 -0
  207. package/src/objects/SendPaymentInput.ts +30 -0
  208. package/src/objects/SendPaymentOutput.ts +22 -0
  209. package/src/objects/Transaction.ts +609 -0
  210. package/src/objects/TransactionFailures.ts +23 -0
  211. package/src/objects/TransactionStatus.ts +23 -0
  212. package/src/objects/TransactionType.ts +31 -0
  213. package/src/objects/TransactionUpdate.ts +67 -0
  214. package/src/objects/WalletDashboard.ts +32 -0
  215. package/src/objects/WebhookEventType.ts +15 -0
  216. package/src/objects/Withdrawal.ts +144 -0
  217. package/src/objects/WithdrawalMode.ts +15 -0
  218. package/src/objects/WithdrawalRequest.ts +224 -0
  219. package/src/objects/WithdrawalRequestStatus.ts +17 -0
  220. package/src/objects/WithdrawalRequestToChannelClosingTransactionsConnection.ts +57 -0
  221. package/src/objects/WithdrawalRequestToChannelOpeningTransactionsConnection.ts +57 -0
  222. package/src/objects/index.ts +108 -0
  223. package/tsconfig.json +5 -0
@@ -0,0 +1,151 @@
1
+ import styled from "@emotion/styled";
2
+
3
+ type LoadingProps = { size: number };
4
+
5
+ export const LoadingSpinner = (props: LoadingProps) => {
6
+ return (
7
+ <Rotate size={props.size}>
8
+ <LoadingSvg />
9
+ </Rotate>
10
+ );
11
+ };
12
+
13
+ const Rotate = styled.div<LoadingProps>`
14
+ display: inline-flex;
15
+ animation: rotate 1s linear infinite;
16
+ width: ${(props) => `${props.size}px`};
17
+ height: ${(props) => `${props.size}px`};
18
+
19
+ @keyframes rotate {
20
+ 0% {
21
+ transform: rotate(0deg);
22
+ }
23
+ 100% {
24
+ transform: rotate(360deg);
25
+ }
26
+ }
27
+ `;
28
+
29
+ const LoadingSvg = () => (
30
+ <svg
31
+ width="100%"
32
+ viewBox="0 0 12 12"
33
+ fill="none"
34
+ xmlns="http://www.w3.org/2000/svg"
35
+ >
36
+ <g clipPath="url(#clip0_1200_7026)">
37
+ <circle cx="6" cy="6" r="5.25" stroke="#333333" strokeWidth="1.5" />
38
+ <path
39
+ d="M6 11.25C3.10051 11.25 0.75 8.89949 0.75 6C0.75 3.10051 3.10051 0.75 6 0.75"
40
+ stroke="black"
41
+ strokeWidth="1.5"
42
+ strokeLinecap="round"
43
+ />
44
+ <mask
45
+ id="mask0_1200_7026"
46
+ style={{ maskType: "alpha" }}
47
+ maskUnits="userSpaceOnUse"
48
+ x="0"
49
+ y="0"
50
+ width="7"
51
+ height="12"
52
+ >
53
+ <path
54
+ d="M6 11.25C3.10051 11.25 0.75 8.89949 0.75 6C0.75 3.10051 3.10051 0.75 6 0.75"
55
+ stroke="black"
56
+ strokeWidth="1.5"
57
+ strokeLinecap="round"
58
+ />
59
+ </mask>
60
+ <g mask="url(#mask0_1200_7026)">
61
+ <g filter="url(#filter0_f_1200_7026)">
62
+ <ellipse
63
+ cx="6.375"
64
+ cy="2.625"
65
+ rx="35.25"
66
+ ry="14.25"
67
+ fill="url(#paint0_radial_1200_7026)"
68
+ />
69
+ </g>
70
+ <g filter="url(#filter1_f_1200_7026)">
71
+ <ellipse
72
+ cx="6.75"
73
+ cy="-5.25"
74
+ rx="23.625"
75
+ ry="19.125"
76
+ fill="url(#paint1_radial_1200_7026)"
77
+ />
78
+ </g>
79
+ </g>
80
+ </g>
81
+ <defs>
82
+ <filter
83
+ id="filter0_f_1200_7026"
84
+ x="-33.375"
85
+ y="-16.125"
86
+ width="79.5"
87
+ height="37.5"
88
+ filterUnits="userSpaceOnUse"
89
+ colorInterpolationFilters="sRGB"
90
+ >
91
+ <feFlood floodOpacity="0" result="BackgroundImageFix" />
92
+ <feBlend
93
+ mode="normal"
94
+ in="SourceGraphic"
95
+ in2="BackgroundImageFix"
96
+ result="shape"
97
+ />
98
+ <feGaussianBlur
99
+ stdDeviation="2.25"
100
+ result="effect1_foregroundBlur_1200_7026"
101
+ />
102
+ </filter>
103
+ <filter
104
+ id="filter1_f_1200_7026"
105
+ x="-21.375"
106
+ y="-28.875"
107
+ width="56.25"
108
+ height="47.25"
109
+ filterUnits="userSpaceOnUse"
110
+ colorInterpolationFilters="sRGB"
111
+ >
112
+ <feFlood floodOpacity="0" result="BackgroundImageFix" />
113
+ <feBlend
114
+ mode="normal"
115
+ in="SourceGraphic"
116
+ in2="BackgroundImageFix"
117
+ result="shape"
118
+ />
119
+ <feGaussianBlur
120
+ stdDeviation="2.25"
121
+ result="effect1_foregroundBlur_1200_7026"
122
+ />
123
+ </filter>
124
+ <radialGradient
125
+ id="paint0_radial_1200_7026"
126
+ cx="0"
127
+ cy="0"
128
+ r="1"
129
+ gradientUnits="userSpaceOnUse"
130
+ gradientTransform="translate(6.44679 2.59598) rotate(161.271) scale(23.5 14.7413)"
131
+ >
132
+ <stop stopColor="#0066FF" />
133
+ <stop offset="1" stopColor="#0066FF" stopOpacity="0" />
134
+ </radialGradient>
135
+ <radialGradient
136
+ id="paint1_radial_1200_7026"
137
+ cx="0"
138
+ cy="0"
139
+ r="1"
140
+ gradientUnits="userSpaceOnUse"
141
+ gradientTransform="translate(6.79812 -5.28895) rotate(145.825) scale(18.0291 17.2834)"
142
+ >
143
+ <stop stopColor="#FFC700" />
144
+ <stop offset="1" stopColor="#BF09FF" stopOpacity="0" />
145
+ </radialGradient>
146
+ <clipPath id="clip0_1200_7026">
147
+ <rect width="12" height="12" fill="white" />
148
+ </clipPath>
149
+ </defs>
150
+ </svg>
151
+ );
@@ -0,0 +1,95 @@
1
+ import styled from "@emotion/styled";
2
+ import { CurrencyUnit, Transaction } from "@lightsparkdev/lightspark-sdk";
3
+ import CurrencyAmountRaw from "./CurrencyAmountRaw";
4
+ import { LoadingSpinner } from "./Loading";
5
+
6
+ const StreamingTransactionChip = (props: {
7
+ transactions: Transaction[];
8
+ streamingDuration: number;
9
+ isStreaming: boolean;
10
+ }) => {
11
+ const filteredTransactions = props.transactions.filter(
12
+ (t) => t.typename === "OutgoingPayment"
13
+ );
14
+ // TODO: Probably need a unit normalization here to ensure they're all sats.
15
+ const totalTransactionAmount = filteredTransactions.reduce(
16
+ (acc, t) => acc + t.amount.originalValue / 1000,
17
+ 0
18
+ );
19
+ return (
20
+ <Wrapper>
21
+ <InnerColumn>
22
+ <TopTextRow>Demo wallet</TopTextRow>
23
+ <BottomTextRow>
24
+ {props.isStreaming ? (
25
+ <>
26
+ <LoadingSpinner size={12} />
27
+ <span style={{ marginInlineStart: "4px" }}>Streaming</span>
28
+ </>
29
+ ) : (
30
+ `${filteredTransactions.length} transactions`
31
+ )}
32
+ </BottomTextRow>
33
+ </InnerColumn>
34
+ <InnerColumn style={{ alignItems: "flex-end" }}>
35
+ <TopTextRow>
36
+ <CurrencyAmountRaw
37
+ value={-totalTransactionAmount}
38
+ unit={CurrencyUnit.SATOSHI}
39
+ symbol
40
+ />
41
+ </TopTextRow>
42
+ <BottomTextRow>
43
+ {secondsToDurationString(Math.round(props.streamingDuration))}
44
+ </BottomTextRow>
45
+ </InnerColumn>
46
+ </Wrapper>
47
+ );
48
+ };
49
+
50
+ const secondsToDurationString = (seconds: number) => {
51
+ const hours = Math.floor(seconds / 3600);
52
+ const minutes = Math.floor((seconds % 3600) / 60);
53
+ const secondsLeft = seconds % 60;
54
+ const hoursString = hours > 0 ? `${hours}h ` : "";
55
+ const minutesString = minutes > 0 ? `${minutes}m ` : "";
56
+ const secondsString = secondsLeft > 0 ? `${secondsLeft}s` : "";
57
+ return `${hoursString}${minutesString}${secondsString}`;
58
+ };
59
+
60
+ const Wrapper = styled.div`
61
+ display: flex;
62
+ flex-direction: row;
63
+ align-items: center;
64
+ justify-content: space-between;
65
+ padding: 18px;
66
+ border: 1.6px solid #f2f2f2;
67
+ border-radius: 12px;
68
+ margin-bottom: 32px;
69
+ margin-top: 16px;
70
+ `;
71
+
72
+ const InnerColumn = styled.div`
73
+ display: flex;
74
+ flex-direction: column;
75
+ `;
76
+
77
+ const TopTextRow = styled.span`
78
+ color: black;
79
+ font-size: 14px;
80
+ font-weight: 700;
81
+ margin-bottom: 6px;
82
+ `;
83
+
84
+ const BottomTextRow = styled.div`
85
+ color: #666666;
86
+ font-size: 14px;
87
+ font-weight: 500;
88
+ display: flex;
89
+ flex-direction: row;
90
+ align-items: center;
91
+ height: 16px;
92
+ justify-content: center;
93
+ `;
94
+
95
+ export default StreamingTransactionChip;
@@ -0,0 +1,93 @@
1
+ import styled from "@emotion/styled";
2
+ import {
3
+ ChannelClosingTransaction,
4
+ ChannelOpeningTransaction,
5
+ IncomingPayment,
6
+ OutgoingPayment,
7
+ RoutingTransaction,
8
+ Transaction,
9
+ Withdrawal,
10
+ } from "@lightsparkdev/lightspark-sdk";
11
+ import { Maybe } from "../common/types";
12
+ import CurrencyAmount from "./CurrencyAmount";
13
+
14
+ export const getTransactionType = (typename: string): string => {
15
+ switch (typename) {
16
+ case "Deposit":
17
+ return "L1 Deposit";
18
+ case "Withdrawal":
19
+ return "L1 Withdraw";
20
+ case "ChannelOpeningTransaction":
21
+ return "Channel Open";
22
+ case "ChannelClosingTransaction":
23
+ return "Channel Close";
24
+ case "RoutingTransaction":
25
+ return "Route";
26
+ case "OutgoingPayment":
27
+ return "Payment";
28
+ case "IncomingPayment":
29
+ return "Payment Request";
30
+ default:
31
+ return "";
32
+ }
33
+ };
34
+
35
+ const getTransactionOtherNode = (transaction: Transaction): Maybe<string> => {
36
+ switch (transaction.typename) {
37
+ case "Withdrawal":
38
+ return (transaction as Withdrawal).originId;
39
+ case "ChannelOpeningTransaction":
40
+ return (transaction as ChannelOpeningTransaction).channelId;
41
+ case "ChannelClosingTransaction":
42
+ return (transaction as ChannelClosingTransaction).channelId;
43
+ case "OutgoingPayment":
44
+ return (transaction as OutgoingPayment).destinationId;
45
+ case "IncomingPayment":
46
+ return (transaction as IncomingPayment).originId;
47
+ case "RoutingTransaction":
48
+ return (transaction as RoutingTransaction).incomingChannelId;
49
+ default:
50
+ return undefined;
51
+ }
52
+ };
53
+
54
+ const TransactionRow = (props: { transaction: Transaction }) => {
55
+ return (
56
+ <TransactionWrapper>
57
+ <div
58
+ style={{
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ marginInlineStart: "8px",
62
+ flex: "1",
63
+ }}
64
+ >
65
+ <div style={{ fontSize: "14px", fontWeight: 500 }}>
66
+ {getTransactionOtherNode(props.transaction)}
67
+ </div>
68
+ <div style={{ fontSize: "10px", color: "#8C8C8C" }}>
69
+ {getTransactionType(props.transaction.typename)}
70
+ </div>
71
+ </div>
72
+ <div style={{ fontSize: "16px", fontWeight: "bold", color: "#17C27C" }}>
73
+ <CurrencyAmount
74
+ amount={props.transaction.amount}
75
+ displayUnit={props.transaction.amount.preferredCurrencyUnit}
76
+ shortNumber
77
+ shortUnit
78
+ symbol
79
+ />
80
+ </div>
81
+ </TransactionWrapper>
82
+ );
83
+ };
84
+
85
+ const TransactionWrapper = styled.div`
86
+ display: flex;
87
+ flex-direction: row;
88
+ align-items: center;
89
+ padding: 16px;
90
+ border-bottom: 1px solid #e8e8e8;
91
+ `;
92
+
93
+ export default TransactionRow;
@@ -0,0 +1,123 @@
1
+ import { ENABLE_YOUTUBE_AND_TWITCH } from "../common/settings";
2
+ import { VideoPlaybackUpdateMessage } from "../types/Messages";
3
+ import {
4
+ updateTransactionRows,
5
+ updateWalletBalances,
6
+ } from "./lightsparkDemoDom";
7
+ import {
8
+ getDomDetailsForLighstparkDemo,
9
+ getDomDetailsForTwitch,
10
+ getDomDetailsForYoutube,
11
+ } from "./videoElementParsers";
12
+
13
+ let currentTrackingDetails: VideoPlaybackUpdateMessage | null = null;
14
+ let timeUpdateListener: (() => void) | null = null;
15
+
16
+ const messageReceived = (
17
+ msg: any,
18
+ sender: chrome.runtime.MessageSender,
19
+ sendResponse: (response: any) => void
20
+ ) => {
21
+ console.log("[content.js]. Message received", msg);
22
+ if (msg.id === "is_video_playing") {
23
+ sendResponse(currentTrackingDetails?.isPlaying || false);
24
+ } else if (
25
+ msg.id === "new_transactions" ||
26
+ msg.id === "transactions_updated"
27
+ ) {
28
+ updateTransactionRows(msg.transactions);
29
+ }
30
+ };
31
+
32
+ const startListeningToVideoEvents = (videoElement: HTMLVideoElement) => {
33
+ console.log(
34
+ `Beginning listener on: ${JSON.stringify(currentTrackingDetails)}`
35
+ );
36
+ if (timeUpdateListener) {
37
+ videoElement.removeEventListener("timeupdate", timeUpdateListener);
38
+ }
39
+ timeUpdateListener = () => {
40
+ console.log("Time update", videoElement.currentTime);
41
+ const prevProgress = currentTrackingDetails?.progress || 0;
42
+ currentTrackingDetails = {
43
+ ...currentTrackingDetails!,
44
+ progress: videoElement.currentTime,
45
+ duration: videoElement.duration,
46
+ prevProgress: prevProgress,
47
+ };
48
+ // No time updates while seeking.
49
+ if (
50
+ videoElement.seeking ||
51
+ Math.abs(videoElement.currentTime - prevProgress) > 2
52
+ )
53
+ return;
54
+ chrome.runtime
55
+ .sendMessage({ id: "video_progress", ...currentTrackingDetails })
56
+ .then((response) => {
57
+ if (response.amountToPay > 0) {
58
+ console.log(`Paying ${response.amountToPay} msats!`);
59
+ }
60
+ });
61
+ };
62
+ const playListener = () => {
63
+ console.log("Play event");
64
+ currentTrackingDetails = {
65
+ ...currentTrackingDetails!,
66
+ isPlaying: true,
67
+ };
68
+ chrome.runtime.sendMessage({ id: "video_play", ...currentTrackingDetails });
69
+ };
70
+ const pauseListener = () => {
71
+ console.log("Pause event");
72
+ currentTrackingDetails = {
73
+ ...currentTrackingDetails!,
74
+ isPlaying: false,
75
+ };
76
+ chrome.runtime.sendMessage({
77
+ id: "video_pause",
78
+ ...currentTrackingDetails,
79
+ });
80
+ };
81
+ videoElement.addEventListener("timeupdate", timeUpdateListener);
82
+ videoElement.addEventListener("play", playListener);
83
+ videoElement.addEventListener("pause", pauseListener);
84
+ };
85
+
86
+ chrome.runtime.onMessage.addListener(messageReceived);
87
+
88
+ const afterDOMLoaded = (numRetries: number) => {
89
+ console.log("[content.js]. DOM loaded");
90
+ const isLightsparkDemo =
91
+ window.location.host.includes("lightspark") ||
92
+ window.location.host.includes("localhost") ||
93
+ window.location.host.includes("sparkinfra.net");
94
+ const parseResponse =
95
+ window.location.host.includes("youtube") && ENABLE_YOUTUBE_AND_TWITCH
96
+ ? getDomDetailsForYoutube()
97
+ : window.location.host.includes("twitch") && ENABLE_YOUTUBE_AND_TWITCH
98
+ ? getDomDetailsForTwitch()
99
+ : getDomDetailsForLighstparkDemo();
100
+
101
+ if (parseResponse) {
102
+ currentTrackingDetails = parseResponse.trackingDetails;
103
+ startListeningToVideoEvents(parseResponse.videoElement);
104
+ chrome.runtime.sendMessage({
105
+ ...parseResponse.trackingDetails,
106
+ id: "video_details",
107
+ });
108
+ if (isLightsparkDemo) {
109
+ updateWalletBalances();
110
+ }
111
+ } else if (numRetries < 10) {
112
+ // Retry in a second to see if the page loads.
113
+ // TODO: Consider whether we need to use a navigation listener to detect video page changes for SPAs where the
114
+ // load event won't re-fire.
115
+ setTimeout(() => afterDOMLoaded(numRetries + 1), 1000);
116
+ }
117
+ };
118
+
119
+ if (document.readyState === "loading") {
120
+ document.addEventListener("DOMContentLoaded", () => afterDOMLoaded(0));
121
+ } else {
122
+ afterDOMLoaded(0);
123
+ }
@@ -0,0 +1,113 @@
1
+ import {
2
+ CurrencyAmount,
3
+ Transaction,
4
+ TransactionStatus,
5
+ } from "@lightsparkdev/lightspark-sdk";
6
+
7
+ type Balances = {
8
+ viewerBalance: CurrencyAmount;
9
+ creatorBalance: CurrencyAmount;
10
+ };
11
+ let initialBalances: Balances | null = null;
12
+
13
+ export const updateWalletBalances = async () => {
14
+ const { balances } = await getWalletBalances();
15
+ initialBalances = balances;
16
+ renderBalances(balances);
17
+ };
18
+
19
+ const renderBalances = async (balances: Balances | null) => {
20
+ if (!balances) {
21
+ return;
22
+ }
23
+ window.postMessage(
24
+ {
25
+ type: "FROM_CONTENT_SCRIPT",
26
+ id: "balances_changed",
27
+ viewerBalance: balances.viewerBalance,
28
+ creatorBalance: balances.creatorBalance,
29
+ },
30
+ "*"
31
+ );
32
+ };
33
+
34
+ const transactions = new Map<string, Transaction>();
35
+
36
+ export const updateTransactionRows = async (
37
+ changedTransactions: Transaction[]
38
+ ) => {
39
+ changedTransactions.forEach((transaction) => {
40
+ transactions.set(transaction.id, transaction);
41
+ });
42
+ const sortedTransactions = Array.from(transactions.values()).sort((a, b) =>
43
+ b.createdAt.localeCompare(a.createdAt)
44
+ );
45
+ window.postMessage(
46
+ {
47
+ type: "FROM_CONTENT_SCRIPT",
48
+ id: "transactions_changed",
49
+ transactions: sortedTransactions,
50
+ },
51
+ "*"
52
+ );
53
+ fakeBalanceChanges();
54
+ };
55
+
56
+ const fakeBalanceChanges = () => {
57
+ if (!initialBalances) {
58
+ return;
59
+ }
60
+
61
+ console.log("initialBalances", JSON.stringify(initialBalances));
62
+ let viewerBalance = initialBalances.viewerBalance;
63
+ let creatorBalance = initialBalances.creatorBalance;
64
+ for (const t of Array.from(transactions.values())) {
65
+ if (t.status !== TransactionStatus.SUCCESS) {
66
+ continue;
67
+ }
68
+
69
+ if (
70
+ t.amount.preferredCurrencyUnit !== viewerBalance.preferredCurrencyUnit ||
71
+ t.amount.originalUnit !== viewerBalance.originalUnit
72
+ ) {
73
+ console.warn("Transaction amount units do not match wallet balance");
74
+ continue;
75
+ }
76
+
77
+ viewerBalance = addCurrencyAmounts(viewerBalance, negativeAmount(t.amount));
78
+ creatorBalance = addCurrencyAmounts(creatorBalance, t.amount);
79
+ }
80
+ console.log("new values", JSON.stringify({ viewerBalance, creatorBalance }));
81
+ renderBalances({ viewerBalance, creatorBalance });
82
+ };
83
+
84
+ const addCurrencyAmounts = (
85
+ amount1: CurrencyAmount,
86
+ amount2: CurrencyAmount
87
+ ) => {
88
+ return {
89
+ ...amount1,
90
+ originalValue: amount1.originalValue + amount2.originalValue,
91
+ preferredCurrencyValueApprox:
92
+ amount1.preferredCurrencyValueApprox +
93
+ amount2.preferredCurrencyValueApprox,
94
+ preferredCurrencyValueRounded:
95
+ amount1.preferredCurrencyValueRounded +
96
+ amount2.preferredCurrencyValueRounded,
97
+ };
98
+ };
99
+
100
+ const negativeAmount = (amount: CurrencyAmount) => {
101
+ return {
102
+ ...amount,
103
+ originalValue: -amount.originalValue,
104
+ preferredCurrencyValueApprox: -amount.preferredCurrencyValueApprox,
105
+ preferredCurrencyValueRounded: -amount.preferredCurrencyValueRounded,
106
+ };
107
+ };
108
+
109
+ const getWalletBalances = () => {
110
+ return chrome.runtime.sendMessage({
111
+ id: "get_streaming_wallet_balances",
112
+ }) as Promise<{ balances: any }>;
113
+ };
@@ -0,0 +1,92 @@
1
+ import { ChannelSource, VideoPlaybackUpdateMessage } from "../types/Messages";
2
+
3
+ type ParseResult = {
4
+ trackingDetails: VideoPlaybackUpdateMessage;
5
+ videoElement: HTMLVideoElement;
6
+ };
7
+
8
+ export const getDomDetailsForYoutube = (): ParseResult | null => {
9
+ const searchParams = new URLSearchParams(window.location.search);
10
+ if (!searchParams.has("v")) {
11
+ return null;
12
+ }
13
+ const userLink = document.querySelector(
14
+ ".ytd-watch-metadata .ytd-channel-name a"
15
+ ) as HTMLAnchorElement;
16
+ const videoElement = document.querySelector(
17
+ "#movie_player video"
18
+ ) as HTMLVideoElement;
19
+ if (!userLink || !videoElement) {
20
+ return null;
21
+ }
22
+ const newTrackingDetails = {
23
+ videoID: searchParams.get("v") || "",
24
+ videoName:
25
+ (document.querySelector('meta[name="title"]') as HTMLMetaElement)
26
+ ?.content ||
27
+ document.querySelector("title")?.textContent ||
28
+ "",
29
+ channelID: userLink.href.slice(
30
+ userLink.href.indexOf("/channel/") + "/channel/".length
31
+ ),
32
+ channelName: userLink.textContent || "",
33
+ channelSource: ChannelSource.youtube,
34
+ progress: 0,
35
+ duration: videoElement?.duration || 0,
36
+ isPlaying: !videoElement.paused,
37
+ };
38
+ return { trackingDetails: newTrackingDetails, videoElement };
39
+ };
40
+
41
+ export const getDomDetailsForTwitch = (): ParseResult | null => {
42
+ let videoID = "livestream";
43
+ if (window.location.pathname.includes("/videos/")) {
44
+ const index = window.location.pathname.lastIndexOf("/videos/");
45
+ videoID = window.location.pathname.slice(index + "/videos/".length);
46
+ // TODO(Jeremy): If we only care about live, probably just return null here.
47
+ }
48
+
49
+ // There may be a better way to do this, but the class names in twitch are obfuscated :-/.
50
+ const userLink = Array.from(
51
+ document.querySelectorAll(".channel-info-content a")
52
+ ).filter((it) => {
53
+ return it.classList.length === 0;
54
+ })[0] as HTMLAnchorElement;
55
+ const videoElement = document.querySelector(
56
+ ".video-player video"
57
+ ) as HTMLVideoElement;
58
+
59
+ const newTrackingDetails = {
60
+ videoID: videoID,
61
+ videoName:
62
+ document.querySelector('*[data-a-target="stream-title"]')?.textContent ||
63
+ "",
64
+ channelID: userLink?.href?.slice(userLink?.href.lastIndexOf("/") + 1) || "",
65
+ channelName: userLink?.textContent || "",
66
+ channelSource: ChannelSource.twitch,
67
+ progress: 0,
68
+ duration: videoElement?.duration || 0,
69
+ isPlaying: !videoElement.paused,
70
+ };
71
+ return { trackingDetails: newTrackingDetails, videoElement };
72
+ };
73
+
74
+ export const getDomDetailsForLighstparkDemo = (): ParseResult | null => {
75
+ const videoElement = document.querySelector(
76
+ "video#stream-sats-video"
77
+ ) as HTMLVideoElement;
78
+ if (!videoElement) {
79
+ return null;
80
+ }
81
+ const newTrackingDetails = {
82
+ videoID: "ls_demo",
83
+ videoName: "Lightspark Streaming Demo",
84
+ channelID: "ls",
85
+ channelName: "Lightspark",
86
+ channelSource: ChannelSource.lightspark,
87
+ progress: 0,
88
+ duration: videoElement?.duration || 0,
89
+ isPlaying: !videoElement.paused,
90
+ };
91
+ return { trackingDetails: newTrackingDetails, videoElement };
92
+ };
@@ -0,0 +1,16 @@
1
+ body {
2
+ width: 300px;
3
+ height: 380px;
4
+ margin: 0;
5
+ font-family: "Montserrat", sans-serif;
6
+ font-weight: 500;
7
+ -webkit-font-smoothing: antialiased;
8
+ -moz-osx-font-smoothing: grayscale;
9
+ }
10
+
11
+ #root {
12
+ height: 100%;
13
+ width: 100%;
14
+ display: flex;
15
+ flex-direction: column;
16
+ }
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom";
3
+ import App from "./App";
4
+ import "./index.css";
5
+
6
+ ReactDOM.render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ document.getElementById("root")
11
+ );