@syra.fm/sdk 0.3.0 → 0.5.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 (314) hide show
  1. package/lib/commonjs/client.js +268 -0
  2. package/lib/commonjs/client.js.map +1 -0
  3. package/lib/commonjs/errors.js +22 -0
  4. package/lib/commonjs/errors.js.map +1 -0
  5. package/lib/commonjs/index.js +63 -0
  6. package/lib/commonjs/index.js.map +1 -0
  7. package/lib/commonjs/index.native.js +28 -0
  8. package/lib/commonjs/index.native.js.map +1 -0
  9. package/lib/commonjs/live/assets/icons/spaces-icon.js +42 -0
  10. package/lib/commonjs/live/assets/icons/spaces-icon.js.map +1 -0
  11. package/lib/commonjs/live/assets/icons/syra-icon.js +43 -0
  12. package/lib/commonjs/live/assets/icons/syra-icon.js.map +1 -0
  13. package/lib/commonjs/live/components/AnimatedPulse.js +38 -0
  14. package/lib/commonjs/live/components/AnimatedPulse.js.map +1 -0
  15. package/lib/commonjs/live/components/AnimatedSpeakerRing.js +90 -0
  16. package/lib/commonjs/live/components/AnimatedSpeakerRing.js.map +1 -0
  17. package/lib/commonjs/live/components/CreateRoomSheet.js +688 -0
  18. package/lib/commonjs/live/components/CreateRoomSheet.js.map +1 -0
  19. package/lib/commonjs/live/components/InsightsPanel.js +333 -0
  20. package/lib/commonjs/live/components/InsightsPanel.js.map +1 -0
  21. package/lib/commonjs/live/components/LiveRoomSheet.js +1195 -0
  22. package/lib/commonjs/live/components/LiveRoomSheet.js.map +1 -0
  23. package/lib/commonjs/live/components/MiniRoomBar.js +161 -0
  24. package/lib/commonjs/live/components/MiniRoomBar.js.map +1 -0
  25. package/lib/commonjs/live/components/PanelHeader.js +57 -0
  26. package/lib/commonjs/live/components/PanelHeader.js.map +1 -0
  27. package/lib/commonjs/live/components/PodcastStreamPicker.js +1190 -0
  28. package/lib/commonjs/live/components/PodcastStreamPicker.js.map +1 -0
  29. package/lib/commonjs/live/components/RecordingCard.js +192 -0
  30. package/lib/commonjs/live/components/RecordingCard.js.map +1 -0
  31. package/lib/commonjs/live/components/RecordingsPanel.js +304 -0
  32. package/lib/commonjs/live/components/RecordingsPanel.js.map +1 -0
  33. package/lib/commonjs/live/components/RoomCard.js +553 -0
  34. package/lib/commonjs/live/components/RoomCard.js.map +1 -0
  35. package/lib/commonjs/live/components/StreamConfigModal.js +656 -0
  36. package/lib/commonjs/live/components/StreamConfigModal.js.map +1 -0
  37. package/lib/commonjs/live/components/StreamConfigPanel.js +822 -0
  38. package/lib/commonjs/live/components/StreamConfigPanel.js.map +1 -0
  39. package/lib/commonjs/live/context/LiveConfigContext.js +46 -0
  40. package/lib/commonjs/live/context/LiveConfigContext.js.map +1 -0
  41. package/lib/commonjs/live/context/LiveRoomContext.js +195 -0
  42. package/lib/commonjs/live/context/LiveRoomContext.js.map +1 -0
  43. package/lib/commonjs/live/hooks/useActiveSpeakers.js +35 -0
  44. package/lib/commonjs/live/hooks/useActiveSpeakers.js.map +1 -0
  45. package/lib/commonjs/live/hooks/useRoomAudio.js +129 -0
  46. package/lib/commonjs/live/hooks/useRoomAudio.js.map +1 -0
  47. package/lib/commonjs/live/hooks/useRoomConnection.js +177 -0
  48. package/lib/commonjs/live/hooks/useRoomConnection.js.map +1 -0
  49. package/lib/commonjs/live/hooks/useRoomManager.js +37 -0
  50. package/lib/commonjs/live/hooks/useRoomManager.js.map +1 -0
  51. package/lib/commonjs/live/hooks/useRoomUsers.js +52 -0
  52. package/lib/commonjs/live/hooks/useRoomUsers.js.map +1 -0
  53. package/lib/commonjs/live/index.js +310 -0
  54. package/lib/commonjs/live/index.js.map +1 -0
  55. package/lib/commonjs/live/services/livekitService.js +20 -0
  56. package/lib/commonjs/live/services/livekitService.js.map +1 -0
  57. package/lib/commonjs/live/services/spaceSocketService.js +181 -0
  58. package/lib/commonjs/live/services/spaceSocketService.js.map +1 -0
  59. package/lib/commonjs/live/services/spacesService.js +642 -0
  60. package/lib/commonjs/live/services/spacesService.js.map +1 -0
  61. package/lib/commonjs/live/types.js +6 -0
  62. package/lib/commonjs/live/types.js.map +1 -0
  63. package/lib/commonjs/live/validation.js +238 -0
  64. package/lib/commonjs/live/validation.js.map +1 -0
  65. package/lib/commonjs/package.json +1 -0
  66. package/lib/commonjs/schema.js +102 -0
  67. package/lib/commonjs/schema.js.map +1 -0
  68. package/lib/module/client.js +264 -0
  69. package/lib/module/client.js.map +1 -0
  70. package/lib/module/errors.js +17 -0
  71. package/lib/module/errors.js.map +1 -0
  72. package/lib/module/index.js +6 -0
  73. package/lib/module/index.js.map +1 -0
  74. package/lib/module/index.native.js +5 -0
  75. package/lib/module/index.native.js.map +1 -0
  76. package/lib/module/live/assets/icons/spaces-icon.js +34 -0
  77. package/lib/module/live/assets/icons/spaces-icon.js.map +1 -0
  78. package/lib/module/live/assets/icons/syra-icon.js +36 -0
  79. package/lib/module/live/assets/icons/syra-icon.js.map +1 -0
  80. package/lib/module/live/components/AnimatedPulse.js +33 -0
  81. package/lib/module/live/components/AnimatedPulse.js.map +1 -0
  82. package/lib/module/live/components/AnimatedSpeakerRing.js +85 -0
  83. package/lib/module/live/components/AnimatedSpeakerRing.js.map +1 -0
  84. package/lib/module/live/components/CreateRoomSheet.js +682 -0
  85. package/lib/module/live/components/CreateRoomSheet.js.map +1 -0
  86. package/lib/module/live/components/InsightsPanel.js +327 -0
  87. package/lib/module/live/components/InsightsPanel.js.map +1 -0
  88. package/lib/module/live/components/LiveRoomSheet.js +1189 -0
  89. package/lib/module/live/components/LiveRoomSheet.js.map +1 -0
  90. package/lib/module/live/components/MiniRoomBar.js +154 -0
  91. package/lib/module/live/components/MiniRoomBar.js.map +1 -0
  92. package/lib/module/live/components/PanelHeader.js +52 -0
  93. package/lib/module/live/components/PanelHeader.js.map +1 -0
  94. package/lib/module/live/components/PodcastStreamPicker.js +1183 -0
  95. package/lib/module/live/components/PodcastStreamPicker.js.map +1 -0
  96. package/lib/module/live/components/RecordingCard.js +187 -0
  97. package/lib/module/live/components/RecordingCard.js.map +1 -0
  98. package/lib/module/live/components/RecordingsPanel.js +298 -0
  99. package/lib/module/live/components/RecordingsPanel.js.map +1 -0
  100. package/lib/module/live/components/RoomCard.js +548 -0
  101. package/lib/module/live/components/RoomCard.js.map +1 -0
  102. package/lib/module/live/components/StreamConfigModal.js +650 -0
  103. package/lib/module/live/components/StreamConfigModal.js.map +1 -0
  104. package/lib/module/live/components/StreamConfigPanel.js +816 -0
  105. package/lib/module/live/components/StreamConfigPanel.js.map +1 -0
  106. package/lib/module/live/context/LiveConfigContext.js +40 -0
  107. package/lib/module/live/context/LiveConfigContext.js.map +1 -0
  108. package/lib/module/live/context/LiveRoomContext.js +189 -0
  109. package/lib/module/live/context/LiveRoomContext.js.map +1 -0
  110. package/lib/module/live/hooks/useActiveSpeakers.js +31 -0
  111. package/lib/module/live/hooks/useActiveSpeakers.js.map +1 -0
  112. package/lib/module/live/hooks/useRoomAudio.js +125 -0
  113. package/lib/module/live/hooks/useRoomAudio.js.map +1 -0
  114. package/lib/module/live/hooks/useRoomConnection.js +173 -0
  115. package/lib/module/live/hooks/useRoomConnection.js.map +1 -0
  116. package/lib/module/live/hooks/useRoomManager.js +32 -0
  117. package/lib/module/live/hooks/useRoomManager.js.map +1 -0
  118. package/lib/module/live/hooks/useRoomUsers.js +46 -0
  119. package/lib/module/live/hooks/useRoomUsers.js.map +1 -0
  120. package/lib/module/live/index.js +38 -0
  121. package/lib/module/live/index.js.map +1 -0
  122. package/lib/module/live/services/livekitService.js +16 -0
  123. package/lib/module/live/services/livekitService.js.map +1 -0
  124. package/lib/module/live/services/spaceSocketService.js +176 -0
  125. package/lib/module/live/services/spaceSocketService.js.map +1 -0
  126. package/lib/module/live/services/spacesService.js +639 -0
  127. package/lib/module/live/services/spacesService.js.map +1 -0
  128. package/lib/module/live/types.js +4 -0
  129. package/lib/module/live/types.js.map +1 -0
  130. package/lib/module/live/validation.js +229 -0
  131. package/lib/module/live/validation.js.map +1 -0
  132. package/lib/module/package.json +1 -0
  133. package/lib/module/schema.js +99 -0
  134. package/lib/module/schema.js.map +1 -0
  135. package/{dist/types → lib/typescript/commonjs}/client.d.ts +36 -1
  136. package/lib/typescript/commonjs/client.d.ts.map +1 -0
  137. package/{dist/types → lib/typescript/commonjs}/errors.d.ts +1 -0
  138. package/lib/typescript/commonjs/errors.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/index.d.ts +6 -0
  140. package/lib/typescript/commonjs/index.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/index.native.d.ts +3 -0
  142. package/lib/typescript/commonjs/index.native.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/live/assets/icons/spaces-icon.d.ts +13 -0
  144. package/lib/typescript/commonjs/live/assets/icons/spaces-icon.d.ts.map +1 -0
  145. package/lib/typescript/commonjs/live/assets/icons/syra-icon.d.ts +13 -0
  146. package/lib/typescript/commonjs/live/assets/icons/syra-icon.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/live/components/AnimatedPulse.d.ts +8 -0
  148. package/lib/typescript/commonjs/live/components/AnimatedPulse.d.ts.map +1 -0
  149. package/lib/typescript/commonjs/live/components/AnimatedSpeakerRing.d.ts +10 -0
  150. package/lib/typescript/commonjs/live/components/AnimatedSpeakerRing.d.ts.map +1 -0
  151. package/lib/typescript/commonjs/live/components/CreateRoomSheet.d.ts +26 -0
  152. package/lib/typescript/commonjs/live/components/CreateRoomSheet.d.ts.map +1 -0
  153. package/lib/typescript/commonjs/live/components/InsightsPanel.d.ts +11 -0
  154. package/lib/typescript/commonjs/live/components/InsightsPanel.d.ts.map +1 -0
  155. package/lib/typescript/commonjs/live/components/LiveRoomSheet.d.ts +11 -0
  156. package/lib/typescript/commonjs/live/components/LiveRoomSheet.d.ts.map +1 -0
  157. package/lib/typescript/commonjs/live/components/MiniRoomBar.d.ts +15 -0
  158. package/lib/typescript/commonjs/live/components/MiniRoomBar.d.ts.map +1 -0
  159. package/lib/typescript/commonjs/live/components/PanelHeader.d.ts +10 -0
  160. package/lib/typescript/commonjs/live/components/PanelHeader.d.ts.map +1 -0
  161. package/lib/typescript/commonjs/live/components/PodcastStreamPicker.d.ts +54 -0
  162. package/lib/typescript/commonjs/live/components/PodcastStreamPicker.d.ts.map +1 -0
  163. package/lib/typescript/commonjs/live/components/RecordingCard.d.ts +11 -0
  164. package/lib/typescript/commonjs/live/components/RecordingCard.d.ts.map +1 -0
  165. package/lib/typescript/commonjs/live/components/RecordingsPanel.d.ts +12 -0
  166. package/lib/typescript/commonjs/live/components/RecordingsPanel.d.ts.map +1 -0
  167. package/lib/typescript/commonjs/live/components/RoomCard.d.ts +38 -0
  168. package/lib/typescript/commonjs/live/components/RoomCard.d.ts.map +1 -0
  169. package/lib/typescript/commonjs/live/components/StreamConfigModal.d.ts +11 -0
  170. package/lib/typescript/commonjs/live/components/StreamConfigModal.d.ts.map +1 -0
  171. package/lib/typescript/commonjs/live/components/StreamConfigPanel.d.ts +13 -0
  172. package/lib/typescript/commonjs/live/components/StreamConfigPanel.d.ts.map +1 -0
  173. package/lib/typescript/commonjs/live/context/LiveConfigContext.d.ts +78 -0
  174. package/lib/typescript/commonjs/live/context/LiveConfigContext.d.ts.map +1 -0
  175. package/lib/typescript/commonjs/live/context/LiveRoomContext.d.ts +12 -0
  176. package/lib/typescript/commonjs/live/context/LiveRoomContext.d.ts.map +1 -0
  177. package/lib/typescript/commonjs/live/hooks/useActiveSpeakers.d.ts +3 -0
  178. package/lib/typescript/commonjs/live/hooks/useActiveSpeakers.d.ts.map +1 -0
  179. package/lib/typescript/commonjs/live/hooks/useRoomAudio.d.ts +16 -0
  180. package/lib/typescript/commonjs/live/hooks/useRoomAudio.d.ts.map +1 -0
  181. package/lib/typescript/commonjs/live/hooks/useRoomConnection.d.ts +29 -0
  182. package/lib/typescript/commonjs/live/hooks/useRoomConnection.d.ts.map +1 -0
  183. package/lib/typescript/commonjs/live/hooks/useRoomManager.d.ts +11 -0
  184. package/lib/typescript/commonjs/live/hooks/useRoomManager.d.ts.map +1 -0
  185. package/lib/typescript/commonjs/live/hooks/useRoomUsers.d.ts +5 -0
  186. package/lib/typescript/commonjs/live/hooks/useRoomUsers.d.ts.map +1 -0
  187. package/lib/typescript/commonjs/live/index.d.ts +24 -0
  188. package/lib/typescript/commonjs/live/index.d.ts.map +1 -0
  189. package/lib/typescript/commonjs/live/services/livekitService.d.ts +7 -0
  190. package/lib/typescript/commonjs/live/services/livekitService.d.ts.map +1 -0
  191. package/lib/typescript/commonjs/live/services/spaceSocketService.d.ts +69 -0
  192. package/lib/typescript/commonjs/live/services/spaceSocketService.d.ts.map +1 -0
  193. package/lib/typescript/commonjs/live/services/spacesService.d.ts +181 -0
  194. package/lib/typescript/commonjs/live/services/spacesService.d.ts.map +1 -0
  195. package/lib/typescript/commonjs/live/types.d.ts +100 -0
  196. package/lib/typescript/commonjs/live/types.d.ts.map +1 -0
  197. package/lib/typescript/commonjs/live/validation.d.ts +658 -0
  198. package/lib/typescript/commonjs/live/validation.d.ts.map +1 -0
  199. package/lib/typescript/commonjs/package.json +1 -0
  200. package/{dist/types → lib/typescript/commonjs}/schema.d.ts +305 -0
  201. package/lib/typescript/commonjs/schema.d.ts.map +1 -0
  202. package/lib/typescript/module/client.d.ts +141 -0
  203. package/lib/typescript/module/client.d.ts.map +1 -0
  204. package/lib/typescript/module/errors.d.ts +9 -0
  205. package/lib/typescript/module/errors.d.ts.map +1 -0
  206. package/lib/typescript/module/index.d.ts +6 -0
  207. package/lib/typescript/module/index.d.ts.map +1 -0
  208. package/lib/typescript/module/index.native.d.ts +3 -0
  209. package/lib/typescript/module/index.native.d.ts.map +1 -0
  210. package/lib/typescript/module/live/assets/icons/spaces-icon.d.ts +13 -0
  211. package/lib/typescript/module/live/assets/icons/spaces-icon.d.ts.map +1 -0
  212. package/lib/typescript/module/live/assets/icons/syra-icon.d.ts +13 -0
  213. package/lib/typescript/module/live/assets/icons/syra-icon.d.ts.map +1 -0
  214. package/lib/typescript/module/live/components/AnimatedPulse.d.ts +8 -0
  215. package/lib/typescript/module/live/components/AnimatedPulse.d.ts.map +1 -0
  216. package/lib/typescript/module/live/components/AnimatedSpeakerRing.d.ts +10 -0
  217. package/lib/typescript/module/live/components/AnimatedSpeakerRing.d.ts.map +1 -0
  218. package/lib/typescript/module/live/components/CreateRoomSheet.d.ts +26 -0
  219. package/lib/typescript/module/live/components/CreateRoomSheet.d.ts.map +1 -0
  220. package/lib/typescript/module/live/components/InsightsPanel.d.ts +11 -0
  221. package/lib/typescript/module/live/components/InsightsPanel.d.ts.map +1 -0
  222. package/lib/typescript/module/live/components/LiveRoomSheet.d.ts +11 -0
  223. package/lib/typescript/module/live/components/LiveRoomSheet.d.ts.map +1 -0
  224. package/lib/typescript/module/live/components/MiniRoomBar.d.ts +15 -0
  225. package/lib/typescript/module/live/components/MiniRoomBar.d.ts.map +1 -0
  226. package/lib/typescript/module/live/components/PanelHeader.d.ts +10 -0
  227. package/lib/typescript/module/live/components/PanelHeader.d.ts.map +1 -0
  228. package/lib/typescript/module/live/components/PodcastStreamPicker.d.ts +54 -0
  229. package/lib/typescript/module/live/components/PodcastStreamPicker.d.ts.map +1 -0
  230. package/lib/typescript/module/live/components/RecordingCard.d.ts +11 -0
  231. package/lib/typescript/module/live/components/RecordingCard.d.ts.map +1 -0
  232. package/lib/typescript/module/live/components/RecordingsPanel.d.ts +12 -0
  233. package/lib/typescript/module/live/components/RecordingsPanel.d.ts.map +1 -0
  234. package/lib/typescript/module/live/components/RoomCard.d.ts +38 -0
  235. package/lib/typescript/module/live/components/RoomCard.d.ts.map +1 -0
  236. package/lib/typescript/module/live/components/StreamConfigModal.d.ts +11 -0
  237. package/lib/typescript/module/live/components/StreamConfigModal.d.ts.map +1 -0
  238. package/lib/typescript/module/live/components/StreamConfigPanel.d.ts +13 -0
  239. package/lib/typescript/module/live/components/StreamConfigPanel.d.ts.map +1 -0
  240. package/lib/typescript/module/live/context/LiveConfigContext.d.ts +78 -0
  241. package/lib/typescript/module/live/context/LiveConfigContext.d.ts.map +1 -0
  242. package/lib/typescript/module/live/context/LiveRoomContext.d.ts +12 -0
  243. package/lib/typescript/module/live/context/LiveRoomContext.d.ts.map +1 -0
  244. package/lib/typescript/module/live/hooks/useActiveSpeakers.d.ts +3 -0
  245. package/lib/typescript/module/live/hooks/useActiveSpeakers.d.ts.map +1 -0
  246. package/lib/typescript/module/live/hooks/useRoomAudio.d.ts +16 -0
  247. package/lib/typescript/module/live/hooks/useRoomAudio.d.ts.map +1 -0
  248. package/lib/typescript/module/live/hooks/useRoomConnection.d.ts +29 -0
  249. package/lib/typescript/module/live/hooks/useRoomConnection.d.ts.map +1 -0
  250. package/lib/typescript/module/live/hooks/useRoomManager.d.ts +11 -0
  251. package/lib/typescript/module/live/hooks/useRoomManager.d.ts.map +1 -0
  252. package/lib/typescript/module/live/hooks/useRoomUsers.d.ts +5 -0
  253. package/lib/typescript/module/live/hooks/useRoomUsers.d.ts.map +1 -0
  254. package/lib/typescript/module/live/index.d.ts +24 -0
  255. package/lib/typescript/module/live/index.d.ts.map +1 -0
  256. package/lib/typescript/module/live/services/livekitService.d.ts +7 -0
  257. package/lib/typescript/module/live/services/livekitService.d.ts.map +1 -0
  258. package/lib/typescript/module/live/services/spaceSocketService.d.ts +69 -0
  259. package/lib/typescript/module/live/services/spaceSocketService.d.ts.map +1 -0
  260. package/lib/typescript/module/live/services/spacesService.d.ts +181 -0
  261. package/lib/typescript/module/live/services/spacesService.d.ts.map +1 -0
  262. package/lib/typescript/module/live/types.d.ts +100 -0
  263. package/lib/typescript/module/live/types.d.ts.map +1 -0
  264. package/lib/typescript/module/live/validation.d.ts +658 -0
  265. package/lib/typescript/module/live/validation.d.ts.map +1 -0
  266. package/lib/typescript/module/package.json +1 -0
  267. package/lib/typescript/module/schema.d.ts +1072 -0
  268. package/lib/typescript/module/schema.d.ts.map +1 -0
  269. package/package.json +133 -21
  270. package/src/client.test.ts +185 -0
  271. package/src/client.ts +125 -0
  272. package/src/index.native.ts +2 -0
  273. package/src/index.ts +4 -0
  274. package/src/live/assets/icons/spaces-icon.tsx +15 -0
  275. package/src/live/assets/icons/syra-icon.tsx +13 -0
  276. package/src/live/assets/sounds/intro.mp3 +0 -0
  277. package/src/live/components/AnimatedPulse.tsx +52 -0
  278. package/src/live/components/AnimatedSpeakerRing.tsx +101 -0
  279. package/src/live/components/CreateRoomSheet.tsx +654 -0
  280. package/src/live/components/InsightsPanel.tsx +202 -0
  281. package/src/live/components/LiveRoomSheet.tsx +969 -0
  282. package/src/live/components/MiniRoomBar.tsx +166 -0
  283. package/src/live/components/PanelHeader.tsx +36 -0
  284. package/src/live/components/PodcastStreamPicker.tsx +1155 -0
  285. package/src/live/components/RecordingCard.tsx +154 -0
  286. package/src/live/components/RecordingsPanel.tsx +251 -0
  287. package/src/live/components/RoomCard.tsx +494 -0
  288. package/src/live/components/StreamConfigModal.tsx +600 -0
  289. package/src/live/components/StreamConfigPanel.tsx +729 -0
  290. package/src/live/context/LiveConfigContext.tsx +96 -0
  291. package/src/live/context/LiveRoomContext.tsx +235 -0
  292. package/src/live/hooks/useActiveSpeakers.ts +39 -0
  293. package/src/live/hooks/useRoomAudio.ts +103 -0
  294. package/src/live/hooks/useRoomConnection.ts +162 -0
  295. package/src/live/hooks/useRoomManager.ts +17 -0
  296. package/src/live/hooks/useRoomUsers.ts +52 -0
  297. package/src/live/index.ts +92 -0
  298. package/src/live/services/livekitService.ts +11 -0
  299. package/src/live/services/spaceSocketService.ts +136 -0
  300. package/src/live/services/spacesService.ts +692 -0
  301. package/src/live/types.ts +119 -0
  302. package/src/live/validation.ts +279 -0
  303. package/src/schema.ts +34 -0
  304. package/dist/cjs/client.js +0 -185
  305. package/dist/cjs/errors.js +0 -16
  306. package/dist/cjs/index.js +0 -14
  307. package/dist/cjs/package.json +0 -3
  308. package/dist/cjs/schema.js +0 -62
  309. package/dist/esm/client.js +0 -181
  310. package/dist/esm/errors.js +0 -12
  311. package/dist/esm/index.js +0 -3
  312. package/dist/esm/package.json +0 -3
  313. package/dist/esm/schema.js +0 -59
  314. package/dist/types/index.d.ts +0 -5
@@ -0,0 +1,1190 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.MusicPicker = MusicPicker;
7
+ exports.PodcastStreamPicker = PodcastStreamPicker;
8
+ var _react = _interopRequireWildcard(require("react"));
9
+ var _reactNative = require("react-native");
10
+ var _MaterialCommunityIcons = _interopRequireDefault(require("@expo/vector-icons/MaterialCommunityIcons"));
11
+ var _LiveConfigContext = require("../context/LiveConfigContext.js");
12
+ var _jsxRuntime = require("react/jsx-runtime");
13
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
14
+ function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
15
+ const SEARCH_DEBOUNCE_MS = 350;
16
+
17
+ /** Public Syra web base — the "Open in Syra" affordance links to `/podcasts/:id`. */
18
+ const SYRA_WEB_BASE_URL = 'https://syra.fm';
19
+
20
+ /**
21
+ * English source copy for every user-facing string in the picker. Hosts with an
22
+ * i18n layer (the Mention frontend) inject `t` via `useLiveConfig()` and these
23
+ * are never read; hosts without one (the standalone live-rooms app) fall back to
24
+ * these. Keys mirror the `agora.podcastStream.*` entries in the app locale files.
25
+ */
26
+ const DEFAULT_STRINGS = {
27
+ 'agora.podcastStream.disclaimer': "You're responsible for having the rights to stream this audio into your room.",
28
+ 'agora.podcastStream.searchPlaceholder': 'Search podcasts',
29
+ 'agora.podcastStream.searchHint': 'Search for a podcast to stream',
30
+ 'agora.podcastStream.pinnedTitle': 'Stream my pinned podcast',
31
+ 'agora.podcastStream.emptyShows': 'No podcasts found',
32
+ 'agora.podcastStream.emptyEpisodes': 'No episodes found',
33
+ 'agora.podcastStream.errorShows': "Couldn't load podcasts. Check your connection and try again.",
34
+ 'agora.podcastStream.errorEpisodes': "Couldn't load episodes. Check your connection and try again.",
35
+ 'agora.podcastStream.retry': 'Retry',
36
+ 'agora.podcastStream.openInSyra': 'Open in Syra',
37
+ 'agora.podcastStream.openFailed': "Couldn't open Syra",
38
+ 'agora.podcastStream.addToQueue': 'Add to queue',
39
+ 'agora.podcastStream.removeFromQueue': 'Remove from queue',
40
+ 'agora.podcastStream.playQueue': 'Play episodes',
41
+ 'agora.podcastStream.clearQueue': 'Clear',
42
+ // Music (listening party). Streaming a full track into a room is a broadcast —
43
+ // the disclaimer makes the host's rights responsibility explicit.
44
+ 'agora.musicStream.disclaimer': "You're responsible for having the rights to broadcast this music into your room.",
45
+ 'agora.musicStream.searchPlaceholder': 'Search music',
46
+ 'agora.musicStream.searchHint': 'Search for a track to play',
47
+ 'agora.musicStream.empty': 'No tracks found',
48
+ 'agora.musicStream.error': "Couldn't load music. Check your connection and try again.",
49
+ 'agora.musicStream.retry': 'Retry',
50
+ 'agora.musicStream.addToQueue': 'Add to queue',
51
+ 'agora.musicStream.removeFromQueue': 'Remove from queue',
52
+ 'agora.musicStream.playQueue': 'Play tracks',
53
+ 'agora.musicStream.clearQueue': 'Clear'
54
+ };
55
+
56
+ /** One episode selected for the up-next queue. The show id is always known here. */
57
+
58
+ function formatDuration(seconds) {
59
+ if (!seconds || seconds <= 0) return '';
60
+ const total = Math.floor(seconds);
61
+ const hours = Math.floor(total / 3600);
62
+ const minutes = Math.floor(total % 3600 / 60);
63
+ const secs = total % 60;
64
+ const paddedSecs = String(secs).padStart(2, '0');
65
+ if (hours > 0) {
66
+ return `${hours}:${String(minutes).padStart(2, '0')}:${paddedSecs}`;
67
+ }
68
+ return `${minutes}:${paddedSecs}`;
69
+ }
70
+ function formatDate(iso) {
71
+ if (!iso) return '';
72
+ const date = new Date(iso);
73
+ if (Number.isNaN(date.getTime())) return '';
74
+ return date.toLocaleDateString(undefined, {
75
+ year: 'numeric',
76
+ month: 'short',
77
+ day: 'numeric'
78
+ });
79
+ }
80
+ const ShowRow = /*#__PURE__*/(0, _react.memo)(function ShowRow({
81
+ show,
82
+ theme,
83
+ AvatarComponent,
84
+ onSelect
85
+ }) {
86
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
87
+ style: styles.row,
88
+ activeOpacity: 0.7,
89
+ onPress: () => onSelect(show),
90
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(AvatarComponent, {
91
+ size: 44,
92
+ source: show.artworkUrl,
93
+ shape: "squircle"
94
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
95
+ style: styles.rowText,
96
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
97
+ style: [styles.rowTitle, {
98
+ color: theme.colors.text
99
+ }],
100
+ numberOfLines: 1,
101
+ children: show.title
102
+ }), !!show.author && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
103
+ style: [styles.rowSubtitle, {
104
+ color: theme.colors.textSecondary
105
+ }],
106
+ numberOfLines: 1,
107
+ children: show.author
108
+ })]
109
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
110
+ name: "chevron-right",
111
+ size: 22,
112
+ color: theme.colors.textSecondary
113
+ })]
114
+ });
115
+ });
116
+ const EpisodeRow = /*#__PURE__*/(0, _react.memo)(function EpisodeRow({
117
+ episode,
118
+ theme,
119
+ AvatarComponent,
120
+ onSelect,
121
+ queueEnabled,
122
+ isQueued,
123
+ onToggleQueue,
124
+ queueActionLabel
125
+ }) {
126
+ const meta = [formatDuration(episode.durationSec), formatDate(episode.publishedAt)].filter(Boolean).join(' · ');
127
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
128
+ style: styles.row,
129
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
130
+ style: styles.rowMain,
131
+ activeOpacity: 0.7,
132
+ onPress: () => onSelect(episode),
133
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(AvatarComponent, {
134
+ size: 44,
135
+ source: episode.artworkUrl,
136
+ shape: "squircle"
137
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
138
+ style: styles.rowText,
139
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
140
+ style: [styles.rowTitle, {
141
+ color: theme.colors.text
142
+ }],
143
+ numberOfLines: 2,
144
+ children: episode.title
145
+ }), !!meta && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
146
+ style: [styles.rowSubtitle, {
147
+ color: theme.colors.textSecondary
148
+ }],
149
+ numberOfLines: 1,
150
+ children: meta
151
+ })]
152
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
153
+ name: "play-circle",
154
+ size: 26,
155
+ color: theme.colors.primary
156
+ })]
157
+ }), queueEnabled && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
158
+ style: styles.queueBtn,
159
+ activeOpacity: 0.7,
160
+ hitSlop: 8,
161
+ onPress: () => onToggleQueue(episode),
162
+ accessibilityRole: "button",
163
+ accessibilityLabel: queueActionLabel,
164
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
165
+ name: isQueued ? 'check-circle' : 'plus-circle-outline',
166
+ size: 24,
167
+ color: isQueued ? theme.colors.primary : theme.colors.textSecondary
168
+ })
169
+ })]
170
+ });
171
+ });
172
+
173
+ /** Failed-request state (distinct from empty) with a Retry affordance. */
174
+ const ErrorState = /*#__PURE__*/(0, _react.memo)(function ErrorState({
175
+ theme,
176
+ message,
177
+ retryLabel,
178
+ onRetry
179
+ }) {
180
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
181
+ style: styles.stateBox,
182
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
183
+ name: "wifi-off",
184
+ size: 32,
185
+ color: theme.colors.textSecondary
186
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
187
+ style: [styles.stateText, {
188
+ color: theme.colors.textSecondary
189
+ }],
190
+ children: message
191
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
192
+ style: [styles.retryBtn, {
193
+ borderColor: theme.colors.border
194
+ }],
195
+ activeOpacity: 0.7,
196
+ onPress: onRetry,
197
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
198
+ name: "refresh",
199
+ size: 16,
200
+ color: theme.colors.primary
201
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
202
+ style: [styles.retryText, {
203
+ color: theme.colors.primary
204
+ }],
205
+ children: retryLabel
206
+ })]
207
+ })]
208
+ });
209
+ });
210
+
211
+ /**
212
+ * Self-contained two-level podcast picker for the live-room stream setup. Level 1
213
+ * is a debounced Syra show search; tapping a show drills into Level 2, its episode
214
+ * list. Tapping an episode reports `(syraPodcastId, episodeId)` to the parent — the
215
+ * picker owns NO room/stream logic. Ships inside @syra.fm/live, so it depends only
216
+ * on `useLiveConfig()` injection and never imports from a host app (`@/`).
217
+ *
218
+ * UX affordances (all backend-field-free): a host copyright disclaimer, an honest
219
+ * error+Retry state distinct from empty results, a one-tap "stream my pinned
220
+ * podcast" quick-start row (when the host exposes `getPinnedPodcast`), and an
221
+ * "Open in Syra" deep link on the episode-list header.
222
+ */
223
+ function PodcastStreamPicker({
224
+ onSelectEpisode,
225
+ onStartQueue
226
+ }) {
227
+ const {
228
+ useTheme,
229
+ roomsService,
230
+ AvatarComponent,
231
+ toast,
232
+ t,
233
+ getPinnedPodcast
234
+ } = (0, _LiveConfigContext.useLiveConfig)();
235
+ const theme = useTheme();
236
+ const tr = (0, _react.useCallback)(key => t ? t(key) : DEFAULT_STRINGS[key] ?? key, [t]);
237
+
238
+ // Up-next queue: only offered when the host wired the multi-episode start.
239
+ const queueEnabled = !!onStartQueue;
240
+ const [queued, setQueued] = (0, _react.useState)([]);
241
+ const queuedIds = (0, _react.useMemo)(() => new Set(queued.map(q => q.episodeId)), [queued]);
242
+
243
+ // Level 1 — show search
244
+ const [query, setQuery] = (0, _react.useState)('');
245
+ const [shows, setShows] = (0, _react.useState)([]);
246
+ const [showsNextOffset, setShowsNextOffset] = (0, _react.useState)(0);
247
+ const [showsHasMore, setShowsHasMore] = (0, _react.useState)(false);
248
+ const [showsLoading, setShowsLoading] = (0, _react.useState)(false);
249
+ const [showsLoadingMore, setShowsLoadingMore] = (0, _react.useState)(false);
250
+ const [showsError, setShowsError] = (0, _react.useState)(false);
251
+ const showsSeqRef = (0, _react.useRef)(0);
252
+ const debounceRef = (0, _react.useRef)(null);
253
+
254
+ // Pinned-podcast quick-start (only when the host exposes profile media).
255
+ const [pinnedPodcast, setPinnedPodcast] = (0, _react.useState)(null);
256
+
257
+ // Level 2 — episode list
258
+ const [selectedShow, setSelectedShow] = (0, _react.useState)(null);
259
+ const [episodes, setEpisodes] = (0, _react.useState)([]);
260
+ const [episodesNextOffset, setEpisodesNextOffset] = (0, _react.useState)(0);
261
+ const [episodesHasMore, setEpisodesHasMore] = (0, _react.useState)(false);
262
+ const [episodesLoading, setEpisodesLoading] = (0, _react.useState)(false);
263
+ const [episodesLoadingMore, setEpisodesLoadingMore] = (0, _react.useState)(false);
264
+ const [episodesError, setEpisodesError] = (0, _react.useState)(false);
265
+ const episodesSeqRef = (0, _react.useRef)(0);
266
+ const runSearch = (0, _react.useCallback)(async (rawQuery, offset) => {
267
+ const trimmed = rawQuery.trim();
268
+ if (!trimmed) {
269
+ showsSeqRef.current += 1;
270
+ setShows([]);
271
+ setShowsHasMore(false);
272
+ setShowsNextOffset(0);
273
+ setShowsLoading(false);
274
+ setShowsLoadingMore(false);
275
+ setShowsError(false);
276
+ return;
277
+ }
278
+ const seq = offset === 0 ? showsSeqRef.current += 1 : showsSeqRef.current;
279
+ if (offset === 0) {
280
+ setShowsLoading(true);
281
+ setShowsError(false);
282
+ } else {
283
+ setShowsLoadingMore(true);
284
+ }
285
+ const result = await roomsService.searchPodcasts(trimmed, offset);
286
+ if (seq !== showsSeqRef.current) return; // superseded by a newer query
287
+
288
+ if (!result.ok) {
289
+ // Only replace the list on a fresh search; a load-more failure keeps the
290
+ // rows the host already has and simply stops paginating.
291
+ if (offset === 0) {
292
+ setShows([]);
293
+ setShowsError(true);
294
+ }
295
+ setShowsHasMore(false);
296
+ setShowsLoading(false);
297
+ setShowsLoadingMore(false);
298
+ return;
299
+ }
300
+ setShows(prev => offset === 0 ? result.items : [...prev, ...result.items]);
301
+ setShowsHasMore(result.hasMore);
302
+ setShowsNextOffset(result.offset + result.items.length);
303
+ setShowsLoading(false);
304
+ setShowsLoadingMore(false);
305
+ }, [roomsService]);
306
+ const handleQueryChange = (0, _react.useCallback)(text => {
307
+ setQuery(text);
308
+ if (debounceRef.current) clearTimeout(debounceRef.current);
309
+ // Show the spinner for the debounce window so a fresh query never briefly
310
+ // renders the "No podcasts found" empty state before the request fires.
311
+ if (text.trim()) {
312
+ setShowsLoading(true);
313
+ setShowsError(false);
314
+ }
315
+ debounceRef.current = setTimeout(() => {
316
+ runSearch(text, 0);
317
+ }, SEARCH_DEBOUNCE_MS);
318
+ }, [runSearch]);
319
+ const handleClearQuery = (0, _react.useCallback)(() => {
320
+ if (debounceRef.current) clearTimeout(debounceRef.current);
321
+ setQuery('');
322
+ runSearch('', 0);
323
+ }, [runSearch]);
324
+ const handleShowsEndReached = (0, _react.useCallback)(() => {
325
+ if (showsLoading || showsLoadingMore || !showsHasMore) return;
326
+ runSearch(query, showsNextOffset);
327
+ }, [showsLoading, showsLoadingMore, showsHasMore, runSearch, query, showsNextOffset]);
328
+ const loadEpisodes = (0, _react.useCallback)(async (syraPodcastId, offset) => {
329
+ const seq = offset === 0 ? episodesSeqRef.current += 1 : episodesSeqRef.current;
330
+ if (offset === 0) {
331
+ setEpisodesLoading(true);
332
+ setEpisodesError(false);
333
+ } else {
334
+ setEpisodesLoadingMore(true);
335
+ }
336
+ const result = await roomsService.getPodcastEpisodes(syraPodcastId, offset);
337
+ if (seq !== episodesSeqRef.current) return; // superseded by a newer show
338
+
339
+ if (!result.ok) {
340
+ if (offset === 0) {
341
+ setEpisodes([]);
342
+ setEpisodesError(true);
343
+ }
344
+ setEpisodesHasMore(false);
345
+ setEpisodesLoading(false);
346
+ setEpisodesLoadingMore(false);
347
+ return;
348
+ }
349
+ setEpisodes(prev => offset === 0 ? result.items : [...prev, ...result.items]);
350
+ setEpisodesHasMore(result.hasMore);
351
+ setEpisodesNextOffset(result.offset + result.items.length);
352
+ setEpisodesLoading(false);
353
+ setEpisodesLoadingMore(false);
354
+ }, [roomsService]);
355
+ const handleSelectShow = (0, _react.useCallback)(show => {
356
+ episodesSeqRef.current += 1;
357
+ setSelectedShow(show);
358
+ setEpisodes([]);
359
+ setEpisodesHasMore(false);
360
+ setEpisodesNextOffset(0);
361
+ setEpisodesError(false);
362
+ loadEpisodes(show.syraPodcastId, 0);
363
+ }, [loadEpisodes]);
364
+ const handleSelectPinned = (0, _react.useCallback)(() => {
365
+ if (!pinnedPodcast) return;
366
+ handleSelectShow({
367
+ syraPodcastId: pinnedPodcast.syraPodcastId,
368
+ title: pinnedPodcast.title ?? tr('agora.podcastStream.pinnedTitle'),
369
+ artworkUrl: pinnedPodcast.artworkUrl
370
+ });
371
+ }, [pinnedPodcast, handleSelectShow, tr]);
372
+ const handleBack = (0, _react.useCallback)(() => {
373
+ episodesSeqRef.current += 1;
374
+ setSelectedShow(null);
375
+ setEpisodes([]);
376
+ setEpisodesHasMore(false);
377
+ setEpisodesNextOffset(0);
378
+ setEpisodesLoading(false);
379
+ setEpisodesLoadingMore(false);
380
+ setEpisodesError(false);
381
+ }, []);
382
+ const handleEpisodesEndReached = (0, _react.useCallback)(() => {
383
+ if (!selectedShow || episodesLoading || episodesLoadingMore || !episodesHasMore) return;
384
+ loadEpisodes(selectedShow.syraPodcastId, episodesNextOffset);
385
+ }, [selectedShow, episodesLoading, episodesLoadingMore, episodesHasMore, loadEpisodes, episodesNextOffset]);
386
+ const handleSelectEpisode = (0, _react.useCallback)(episode => {
387
+ if (!selectedShow) return;
388
+ onSelectEpisode(selectedShow.syraPodcastId, episode.episodeId);
389
+ }, [selectedShow, onSelectEpisode]);
390
+
391
+ // Toggle an episode in/out of the local up-next queue. Items keep their own
392
+ // show id, so a host can queue across multiple shows before launching.
393
+ const handleToggleQueue = (0, _react.useCallback)(episode => {
394
+ if (!selectedShow) return;
395
+ const showId = selectedShow.syraPodcastId;
396
+ setQueued(prev => prev.some(q => q.episodeId === episode.episodeId) ? prev.filter(q => q.episodeId !== episode.episodeId) : [...prev, {
397
+ syraPodcastId: showId,
398
+ episodeId: episode.episodeId
399
+ }]);
400
+ }, [selectedShow]);
401
+ const handleClearQueue = (0, _react.useCallback)(() => setQueued([]), []);
402
+ const handleStartQueue = (0, _react.useCallback)(() => {
403
+ if (queued.length === 0) return;
404
+ onStartQueue?.(queued);
405
+ setQueued([]);
406
+ }, [queued, onStartQueue]);
407
+ const handleOpenInSyra = (0, _react.useCallback)(() => {
408
+ if (!selectedShow) return;
409
+ _reactNative.Linking.openURL(`${SYRA_WEB_BASE_URL}/podcasts/${selectedShow.syraPodcastId}`).catch(() => toast.error(tr('agora.podcastStream.openFailed')));
410
+ }, [selectedShow, toast, tr]);
411
+
412
+ // Resolve the viewer's pinned podcast once on mount (host-provided; optional).
413
+ (0, _react.useEffect)(() => {
414
+ if (!getPinnedPodcast) return;
415
+ let cancelled = false;
416
+ getPinnedPodcast().then(podcast => {
417
+ if (!cancelled) setPinnedPodcast(podcast);
418
+ }, () => {
419
+ if (!cancelled) setPinnedPodcast(null);
420
+ });
421
+ return () => {
422
+ cancelled = true;
423
+ };
424
+ }, [getPinnedPodcast]);
425
+
426
+ // Cancel any pending debounced search when the picker unmounts.
427
+ (0, _react.useEffect)(() => {
428
+ return () => {
429
+ if (debounceRef.current) clearTimeout(debounceRef.current);
430
+ };
431
+ }, []);
432
+ const hasQuery = query.trim().length > 0;
433
+ const showQueueFooter = queueEnabled && queued.length > 0;
434
+ const listContentStyle = showQueueFooter ? [styles.listContent, styles.listContentWithFooter] : styles.listContent;
435
+
436
+ // Floats over the bottom of either level; queued items persist across shows
437
+ // so the host can review/launch after adding from multiple shows.
438
+ const queueFooter = showQueueFooter ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
439
+ style: [styles.queueFooter, {
440
+ backgroundColor: theme.colors.background,
441
+ borderTopColor: theme.colors.border
442
+ }],
443
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
444
+ style: styles.queueClearBtn,
445
+ activeOpacity: 0.7,
446
+ hitSlop: 8,
447
+ onPress: handleClearQueue,
448
+ accessibilityRole: "button",
449
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
450
+ style: [styles.queueClearText, {
451
+ color: theme.colors.textSecondary
452
+ }],
453
+ children: tr('agora.podcastStream.clearQueue')
454
+ })
455
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
456
+ style: [styles.queuePlayBtn, {
457
+ backgroundColor: theme.colors.primary
458
+ }],
459
+ activeOpacity: 0.85,
460
+ onPress: handleStartQueue,
461
+ accessibilityRole: "button",
462
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
463
+ name: "play",
464
+ size: 18,
465
+ color: "#FFFFFF"
466
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
467
+ style: styles.queuePlayText,
468
+ children: [tr('agora.podcastStream.playQueue'), " (", queued.length, ")"]
469
+ })]
470
+ })]
471
+ }) : null;
472
+ if (selectedShow) {
473
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
474
+ style: styles.container,
475
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
476
+ style: styles.backHeader,
477
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
478
+ style: styles.backHeaderMain,
479
+ activeOpacity: 0.7,
480
+ onPress: handleBack,
481
+ hitSlop: 8,
482
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
483
+ name: "chevron-left",
484
+ size: 24,
485
+ color: theme.colors.text
486
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
487
+ style: [styles.backTitle, {
488
+ color: theme.colors.text
489
+ }],
490
+ numberOfLines: 1,
491
+ children: selectedShow.title
492
+ })]
493
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
494
+ style: styles.openBtn,
495
+ activeOpacity: 0.7,
496
+ onPress: handleOpenInSyra,
497
+ hitSlop: 8,
498
+ accessibilityRole: "link",
499
+ accessibilityLabel: tr('agora.podcastStream.openInSyra'),
500
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
501
+ name: "open-in-new",
502
+ size: 20,
503
+ color: theme.colors.textSecondary
504
+ })
505
+ })]
506
+ }), episodesLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
507
+ style: styles.stateBox,
508
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
509
+ size: "small",
510
+ color: theme.colors.primary
511
+ })
512
+ }) : episodesError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ErrorState, {
513
+ theme: theme,
514
+ message: tr('agora.podcastStream.errorEpisodes'),
515
+ retryLabel: tr('agora.podcastStream.retry'),
516
+ onRetry: () => loadEpisodes(selectedShow.syraPodcastId, 0)
517
+ }) : episodes.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
518
+ style: styles.stateBox,
519
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
520
+ name: "playlist-remove",
521
+ size: 32,
522
+ color: theme.colors.textSecondary
523
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
524
+ style: [styles.stateText, {
525
+ color: theme.colors.textSecondary
526
+ }],
527
+ children: tr('agora.podcastStream.emptyEpisodes')
528
+ })]
529
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
530
+ data: episodes,
531
+ keyExtractor: item => item.episodeId,
532
+ renderItem: ({
533
+ item
534
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(EpisodeRow, {
535
+ episode: item,
536
+ theme: theme,
537
+ AvatarComponent: AvatarComponent,
538
+ onSelect: handleSelectEpisode,
539
+ queueEnabled: queueEnabled,
540
+ isQueued: queuedIds.has(item.episodeId),
541
+ onToggleQueue: handleToggleQueue,
542
+ queueActionLabel: tr(queuedIds.has(item.episodeId) ? 'agora.podcastStream.removeFromQueue' : 'agora.podcastStream.addToQueue')
543
+ }),
544
+ showsVerticalScrollIndicator: false,
545
+ keyboardShouldPersistTaps: "handled",
546
+ onEndReached: handleEpisodesEndReached,
547
+ onEndReachedThreshold: 0.4,
548
+ contentContainerStyle: listContentStyle,
549
+ ListFooterComponent: episodesLoadingMore ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
550
+ style: styles.footerLoader,
551
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
552
+ size: "small",
553
+ color: theme.colors.primary
554
+ })
555
+ }) : null
556
+ }), queueFooter]
557
+ });
558
+ }
559
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
560
+ style: styles.container,
561
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
562
+ style: styles.disclaimer,
563
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
564
+ name: "shield-alert-outline",
565
+ size: 15,
566
+ color: theme.colors.textSecondary
567
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
568
+ style: [styles.disclaimerText, {
569
+ color: theme.colors.textSecondary
570
+ }],
571
+ children: tr('agora.podcastStream.disclaimer')
572
+ })]
573
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
574
+ style: styles.searchWrap,
575
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
576
+ style: [styles.searchRow, {
577
+ backgroundColor: `${theme.colors.card}80`,
578
+ borderColor: theme.colors.border
579
+ }],
580
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
581
+ name: "magnify",
582
+ size: 18,
583
+ color: theme.colors.textSecondary
584
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
585
+ style: [styles.searchInput, {
586
+ color: theme.colors.text
587
+ }],
588
+ placeholder: tr('agora.podcastStream.searchPlaceholder'),
589
+ placeholderTextColor: theme.colors.textSecondary,
590
+ value: query,
591
+ onChangeText: handleQueryChange,
592
+ autoCapitalize: "none",
593
+ autoCorrect: false,
594
+ returnKeyType: "search"
595
+ }), query.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
596
+ onPress: handleClearQuery,
597
+ hitSlop: 8,
598
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
599
+ name: "close-circle",
600
+ size: 18,
601
+ color: theme.colors.textSecondary
602
+ })
603
+ })]
604
+ })
605
+ }), !hasQuery ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
606
+ children: [pinnedPodcast && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
607
+ style: [styles.pinnedRow, {
608
+ backgroundColor: `${theme.colors.primary}14`,
609
+ borderColor: theme.colors.border
610
+ }],
611
+ activeOpacity: 0.7,
612
+ onPress: handleSelectPinned,
613
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
614
+ style: [styles.pinnedIcon, {
615
+ backgroundColor: `${theme.colors.primary}22`
616
+ }],
617
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
618
+ name: "flash",
619
+ size: 20,
620
+ color: theme.colors.primary
621
+ })
622
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
623
+ style: styles.rowText,
624
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
625
+ style: [styles.rowTitle, {
626
+ color: theme.colors.text
627
+ }],
628
+ numberOfLines: 1,
629
+ children: tr('agora.podcastStream.pinnedTitle')
630
+ }), !!pinnedPodcast.title && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
631
+ style: [styles.rowSubtitle, {
632
+ color: theme.colors.textSecondary
633
+ }],
634
+ numberOfLines: 1,
635
+ children: pinnedPodcast.title
636
+ })]
637
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
638
+ name: "chevron-right",
639
+ size: 22,
640
+ color: theme.colors.textSecondary
641
+ })]
642
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
643
+ style: styles.stateBox,
644
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
645
+ name: "podcast",
646
+ size: 32,
647
+ color: theme.colors.textSecondary
648
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
649
+ style: [styles.stateText, {
650
+ color: theme.colors.textSecondary
651
+ }],
652
+ children: tr('agora.podcastStream.searchHint')
653
+ })]
654
+ })]
655
+ }) : showsError ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ErrorState, {
656
+ theme: theme,
657
+ message: tr('agora.podcastStream.errorShows'),
658
+ retryLabel: tr('agora.podcastStream.retry'),
659
+ onRetry: () => runSearch(query, 0)
660
+ }) : showsLoading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
661
+ style: styles.stateBox,
662
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
663
+ size: "small",
664
+ color: theme.colors.primary
665
+ })
666
+ }) : shows.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
667
+ style: styles.stateBox,
668
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
669
+ name: "magnify-close",
670
+ size: 32,
671
+ color: theme.colors.textSecondary
672
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
673
+ style: [styles.stateText, {
674
+ color: theme.colors.textSecondary
675
+ }],
676
+ children: tr('agora.podcastStream.emptyShows')
677
+ })]
678
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
679
+ data: shows,
680
+ keyExtractor: item => item.syraPodcastId,
681
+ renderItem: ({
682
+ item
683
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(ShowRow, {
684
+ show: item,
685
+ theme: theme,
686
+ AvatarComponent: AvatarComponent,
687
+ onSelect: handleSelectShow
688
+ }),
689
+ showsVerticalScrollIndicator: false,
690
+ keyboardShouldPersistTaps: "handled",
691
+ onEndReached: handleShowsEndReached,
692
+ onEndReachedThreshold: 0.4,
693
+ contentContainerStyle: listContentStyle,
694
+ ListFooterComponent: showsLoadingMore ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
695
+ style: styles.footerLoader,
696
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
697
+ size: "small",
698
+ color: theme.colors.primary
699
+ })
700
+ }) : null
701
+ }), queueFooter]
702
+ });
703
+ }
704
+ const MusicTrackRow = /*#__PURE__*/(0, _react.memo)(function MusicTrackRow({
705
+ track,
706
+ theme,
707
+ AvatarComponent,
708
+ onSelect,
709
+ isQueued,
710
+ onToggleQueue,
711
+ queueActionLabel
712
+ }) {
713
+ const meta = [track.artist, formatDuration(track.durationSec)].filter(Boolean).join(' · ');
714
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
715
+ style: styles.row,
716
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
717
+ style: styles.rowMain,
718
+ activeOpacity: 0.7,
719
+ onPress: () => onSelect(track),
720
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(AvatarComponent, {
721
+ size: 44,
722
+ source: track.artworkUrl,
723
+ shape: "squircle"
724
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
725
+ style: styles.rowText,
726
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
727
+ style: [styles.rowTitle, {
728
+ color: theme.colors.text
729
+ }],
730
+ numberOfLines: 2,
731
+ children: track.title
732
+ }), !!meta && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
733
+ style: [styles.rowSubtitle, {
734
+ color: theme.colors.textSecondary
735
+ }],
736
+ numberOfLines: 1,
737
+ children: meta
738
+ })]
739
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
740
+ name: "play-circle",
741
+ size: 26,
742
+ color: theme.colors.primary
743
+ })]
744
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
745
+ style: styles.queueBtn,
746
+ activeOpacity: 0.7,
747
+ hitSlop: 8,
748
+ onPress: () => onToggleQueue(track),
749
+ accessibilityRole: "button",
750
+ accessibilityLabel: queueActionLabel,
751
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
752
+ name: isQueued ? 'check-circle' : 'plus-circle-outline',
753
+ size: 24,
754
+ color: isQueued ? theme.colors.primary : theme.colors.textSecondary
755
+ })
756
+ })]
757
+ });
758
+ });
759
+ /**
760
+ * Self-contained single-level music picker for the live-room "listening party".
761
+ * A debounced Syra catalog search; tapping a track reports its `trackId` to the
762
+ * parent (the picker owns NO room/stream logic and never sees an audio URL — the
763
+ * backend resolves the playable source server-side). Ships inside the shared
764
+ * package, so it depends only on `useLiveConfig()` injection and never imports
765
+ * from a host app (`@/`).
766
+ *
767
+ * Mirrors {@link PodcastStreamPicker}: a host copyright/broadcast disclaimer, an
768
+ * honest error+Retry state distinct from empty results, and an optional up-next
769
+ * queue (per-row "+" plus a floating "Play (N)" footer).
770
+ */
771
+ function MusicPicker({
772
+ onSelectTrack,
773
+ onStartQueue
774
+ }) {
775
+ const {
776
+ useTheme,
777
+ roomsService,
778
+ AvatarComponent,
779
+ t
780
+ } = (0, _LiveConfigContext.useLiveConfig)();
781
+ const theme = useTheme();
782
+ const tr = (0, _react.useCallback)(key => t ? t(key) : DEFAULT_STRINGS[key] ?? key, [t]);
783
+ const queueEnabled = !!onStartQueue;
784
+ const [queued, setQueued] = (0, _react.useState)([]);
785
+ const queuedIds = (0, _react.useMemo)(() => new Set(queued), [queued]);
786
+ const [query, setQuery] = (0, _react.useState)('');
787
+ const [tracks, setTracks] = (0, _react.useState)([]);
788
+ const [nextOffset, setNextOffset] = (0, _react.useState)(0);
789
+ const [hasMore, setHasMore] = (0, _react.useState)(false);
790
+ const [loading, setLoading] = (0, _react.useState)(false);
791
+ const [loadingMore, setLoadingMore] = (0, _react.useState)(false);
792
+ const [error, setError] = (0, _react.useState)(false);
793
+ const seqRef = (0, _react.useRef)(0);
794
+ const debounceRef = (0, _react.useRef)(null);
795
+ const runSearch = (0, _react.useCallback)(async (rawQuery, offset) => {
796
+ const trimmed = rawQuery.trim();
797
+ if (!trimmed) {
798
+ seqRef.current += 1;
799
+ setTracks([]);
800
+ setHasMore(false);
801
+ setNextOffset(0);
802
+ setLoading(false);
803
+ setLoadingMore(false);
804
+ setError(false);
805
+ return;
806
+ }
807
+ const seq = offset === 0 ? seqRef.current += 1 : seqRef.current;
808
+ if (offset === 0) {
809
+ setLoading(true);
810
+ setError(false);
811
+ } else {
812
+ setLoadingMore(true);
813
+ }
814
+ const result = await roomsService.searchMusic(trimmed, offset);
815
+ if (seq !== seqRef.current) return; // superseded by a newer query
816
+
817
+ if (!result.ok) {
818
+ if (offset === 0) {
819
+ setTracks([]);
820
+ setError(true);
821
+ }
822
+ setHasMore(false);
823
+ setLoading(false);
824
+ setLoadingMore(false);
825
+ return;
826
+ }
827
+ setTracks(prev => offset === 0 ? result.items : [...prev, ...result.items]);
828
+ setHasMore(result.hasMore);
829
+ setNextOffset(result.offset + result.items.length);
830
+ setLoading(false);
831
+ setLoadingMore(false);
832
+ }, [roomsService]);
833
+ const handleQueryChange = (0, _react.useCallback)(text => {
834
+ setQuery(text);
835
+ if (debounceRef.current) clearTimeout(debounceRef.current);
836
+ if (text.trim()) {
837
+ setLoading(true);
838
+ setError(false);
839
+ }
840
+ debounceRef.current = setTimeout(() => {
841
+ runSearch(text, 0);
842
+ }, SEARCH_DEBOUNCE_MS);
843
+ }, [runSearch]);
844
+ const handleClearQuery = (0, _react.useCallback)(() => {
845
+ if (debounceRef.current) clearTimeout(debounceRef.current);
846
+ setQuery('');
847
+ runSearch('', 0);
848
+ }, [runSearch]);
849
+ const handleEndReached = (0, _react.useCallback)(() => {
850
+ if (loading || loadingMore || !hasMore) return;
851
+ runSearch(query, nextOffset);
852
+ }, [loading, loadingMore, hasMore, runSearch, query, nextOffset]);
853
+ const handleSelectTrack = (0, _react.useCallback)(track => onSelectTrack(track.trackId), [onSelectTrack]);
854
+ const handleToggleQueue = (0, _react.useCallback)(track => {
855
+ setQueued(prev => prev.includes(track.trackId) ? prev.filter(id => id !== track.trackId) : [...prev, track.trackId]);
856
+ }, []);
857
+ const handleClearQueue = (0, _react.useCallback)(() => setQueued([]), []);
858
+ const handleStartQueue = (0, _react.useCallback)(() => {
859
+ if (queued.length === 0) return;
860
+ onStartQueue?.(queued);
861
+ setQueued([]);
862
+ }, [queued, onStartQueue]);
863
+
864
+ // Cancel any pending debounced search when the picker unmounts.
865
+ (0, _react.useEffect)(() => {
866
+ return () => {
867
+ if (debounceRef.current) clearTimeout(debounceRef.current);
868
+ };
869
+ }, []);
870
+ const hasQuery = query.trim().length > 0;
871
+ const showQueueFooter = queueEnabled && queued.length > 0;
872
+ const listContentStyle = showQueueFooter ? [styles.listContent, styles.listContentWithFooter] : styles.listContent;
873
+ const queueFooter = showQueueFooter ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
874
+ style: [styles.queueFooter, {
875
+ backgroundColor: theme.colors.background,
876
+ borderTopColor: theme.colors.border
877
+ }],
878
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
879
+ style: styles.queueClearBtn,
880
+ activeOpacity: 0.7,
881
+ hitSlop: 8,
882
+ onPress: handleClearQueue,
883
+ accessibilityRole: "button",
884
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
885
+ style: [styles.queueClearText, {
886
+ color: theme.colors.textSecondary
887
+ }],
888
+ children: tr('agora.musicStream.clearQueue')
889
+ })
890
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.TouchableOpacity, {
891
+ style: [styles.queuePlayBtn, {
892
+ backgroundColor: theme.colors.primary
893
+ }],
894
+ activeOpacity: 0.85,
895
+ onPress: handleStartQueue,
896
+ accessibilityRole: "button",
897
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
898
+ name: "play",
899
+ size: 18,
900
+ color: "#FFFFFF"
901
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.Text, {
902
+ style: styles.queuePlayText,
903
+ children: [tr('agora.musicStream.playQueue'), " (", queued.length, ")"]
904
+ })]
905
+ })]
906
+ }) : null;
907
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
908
+ style: styles.container,
909
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
910
+ style: styles.disclaimer,
911
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
912
+ name: "shield-alert-outline",
913
+ size: 15,
914
+ color: theme.colors.textSecondary
915
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
916
+ style: [styles.disclaimerText, {
917
+ color: theme.colors.textSecondary
918
+ }],
919
+ children: tr('agora.musicStream.disclaimer')
920
+ })]
921
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
922
+ style: styles.searchWrap,
923
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
924
+ style: [styles.searchRow, {
925
+ backgroundColor: `${theme.colors.card}80`,
926
+ borderColor: theme.colors.border
927
+ }],
928
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
929
+ name: "magnify",
930
+ size: 18,
931
+ color: theme.colors.textSecondary
932
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TextInput, {
933
+ style: [styles.searchInput, {
934
+ color: theme.colors.text
935
+ }],
936
+ placeholder: tr('agora.musicStream.searchPlaceholder'),
937
+ placeholderTextColor: theme.colors.textSecondary,
938
+ value: query,
939
+ onChangeText: handleQueryChange,
940
+ autoCapitalize: "none",
941
+ autoCorrect: false,
942
+ returnKeyType: "search"
943
+ }), query.length > 0 && /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
944
+ onPress: handleClearQuery,
945
+ hitSlop: 8,
946
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
947
+ name: "close-circle",
948
+ size: 18,
949
+ color: theme.colors.textSecondary
950
+ })
951
+ })]
952
+ })
953
+ }), !hasQuery ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
954
+ style: styles.stateBox,
955
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
956
+ name: "music-note",
957
+ size: 32,
958
+ color: theme.colors.textSecondary
959
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
960
+ style: [styles.stateText, {
961
+ color: theme.colors.textSecondary
962
+ }],
963
+ children: tr('agora.musicStream.searchHint')
964
+ })]
965
+ }) : error ? /*#__PURE__*/(0, _jsxRuntime.jsx)(ErrorState, {
966
+ theme: theme,
967
+ message: tr('agora.musicStream.error'),
968
+ retryLabel: tr('agora.musicStream.retry'),
969
+ onRetry: () => runSearch(query, 0)
970
+ }) : loading ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
971
+ style: styles.stateBox,
972
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
973
+ size: "small",
974
+ color: theme.colors.primary
975
+ })
976
+ }) : tracks.length === 0 ? /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
977
+ style: styles.stateBox,
978
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_MaterialCommunityIcons.default, {
979
+ name: "magnify-close",
980
+ size: 32,
981
+ color: theme.colors.textSecondary
982
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
983
+ style: [styles.stateText, {
984
+ color: theme.colors.textSecondary
985
+ }],
986
+ children: tr('agora.musicStream.empty')
987
+ })]
988
+ }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.FlatList, {
989
+ data: tracks,
990
+ keyExtractor: item => item.trackId,
991
+ renderItem: ({
992
+ item
993
+ }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(MusicTrackRow, {
994
+ track: item,
995
+ theme: theme,
996
+ AvatarComponent: AvatarComponent,
997
+ onSelect: handleSelectTrack,
998
+ isQueued: queuedIds.has(item.trackId),
999
+ onToggleQueue: handleToggleQueue,
1000
+ queueActionLabel: tr(queuedIds.has(item.trackId) ? 'agora.musicStream.removeFromQueue' : 'agora.musicStream.addToQueue')
1001
+ }),
1002
+ showsVerticalScrollIndicator: false,
1003
+ keyboardShouldPersistTaps: "handled",
1004
+ onEndReached: handleEndReached,
1005
+ onEndReachedThreshold: 0.4,
1006
+ contentContainerStyle: listContentStyle,
1007
+ ListFooterComponent: loadingMore ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.View, {
1008
+ style: styles.footerLoader,
1009
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.ActivityIndicator, {
1010
+ size: "small",
1011
+ color: theme.colors.primary
1012
+ })
1013
+ }) : null
1014
+ }), queueFooter]
1015
+ });
1016
+ }
1017
+ const styles = _reactNative.StyleSheet.create({
1018
+ container: {
1019
+ flex: 1
1020
+ },
1021
+ disclaimer: {
1022
+ flexDirection: 'row',
1023
+ alignItems: 'flex-start',
1024
+ gap: 8,
1025
+ paddingHorizontal: 16,
1026
+ paddingTop: 12
1027
+ },
1028
+ disclaimerText: {
1029
+ flex: 1,
1030
+ fontSize: 12,
1031
+ lineHeight: 16
1032
+ },
1033
+ searchWrap: {
1034
+ paddingHorizontal: 16,
1035
+ paddingTop: 12,
1036
+ paddingBottom: 8
1037
+ },
1038
+ searchRow: {
1039
+ flexDirection: 'row',
1040
+ alignItems: 'center',
1041
+ gap: 8,
1042
+ borderWidth: 1,
1043
+ borderRadius: 10,
1044
+ paddingHorizontal: 12,
1045
+ paddingVertical: 10
1046
+ },
1047
+ searchInput: {
1048
+ flex: 1,
1049
+ fontSize: 15,
1050
+ padding: 0
1051
+ },
1052
+ backHeader: {
1053
+ flexDirection: 'row',
1054
+ alignItems: 'center',
1055
+ paddingHorizontal: 12,
1056
+ paddingTop: 12,
1057
+ paddingBottom: 8
1058
+ },
1059
+ backHeaderMain: {
1060
+ flex: 1,
1061
+ flexDirection: 'row',
1062
+ alignItems: 'center',
1063
+ gap: 2
1064
+ },
1065
+ backTitle: {
1066
+ flex: 1,
1067
+ fontSize: 16,
1068
+ fontWeight: '600'
1069
+ },
1070
+ openBtn: {
1071
+ padding: 6,
1072
+ marginLeft: 4
1073
+ },
1074
+ listContent: {
1075
+ paddingBottom: 24
1076
+ },
1077
+ listContentWithFooter: {
1078
+ paddingBottom: 88
1079
+ },
1080
+ row: {
1081
+ flexDirection: 'row',
1082
+ alignItems: 'center',
1083
+ gap: 12,
1084
+ paddingHorizontal: 16,
1085
+ paddingVertical: 10
1086
+ },
1087
+ rowMain: {
1088
+ flex: 1,
1089
+ flexDirection: 'row',
1090
+ alignItems: 'center',
1091
+ gap: 12
1092
+ },
1093
+ queueBtn: {
1094
+ padding: 4
1095
+ },
1096
+ pinnedRow: {
1097
+ flexDirection: 'row',
1098
+ alignItems: 'center',
1099
+ gap: 12,
1100
+ marginHorizontal: 16,
1101
+ marginTop: 4,
1102
+ marginBottom: 8,
1103
+ paddingHorizontal: 12,
1104
+ paddingVertical: 10,
1105
+ borderWidth: 1,
1106
+ borderRadius: 12
1107
+ },
1108
+ pinnedIcon: {
1109
+ width: 44,
1110
+ height: 44,
1111
+ borderRadius: 12,
1112
+ alignItems: 'center',
1113
+ justifyContent: 'center'
1114
+ },
1115
+ rowText: {
1116
+ flex: 1,
1117
+ gap: 2
1118
+ },
1119
+ rowTitle: {
1120
+ fontSize: 15,
1121
+ fontWeight: '600'
1122
+ },
1123
+ rowSubtitle: {
1124
+ fontSize: 13
1125
+ },
1126
+ stateBox: {
1127
+ alignItems: 'center',
1128
+ justifyContent: 'center',
1129
+ gap: 10,
1130
+ paddingVertical: 40
1131
+ },
1132
+ stateText: {
1133
+ fontSize: 14,
1134
+ textAlign: 'center',
1135
+ paddingHorizontal: 32
1136
+ },
1137
+ retryBtn: {
1138
+ flexDirection: 'row',
1139
+ alignItems: 'center',
1140
+ gap: 6,
1141
+ borderWidth: 1,
1142
+ borderRadius: 10,
1143
+ paddingHorizontal: 16,
1144
+ paddingVertical: 8,
1145
+ marginTop: 4
1146
+ },
1147
+ retryText: {
1148
+ fontSize: 14,
1149
+ fontWeight: '600'
1150
+ },
1151
+ footerLoader: {
1152
+ paddingVertical: 16
1153
+ },
1154
+ queueFooter: {
1155
+ position: 'absolute',
1156
+ left: 0,
1157
+ right: 0,
1158
+ bottom: 0,
1159
+ flexDirection: 'row',
1160
+ alignItems: 'center',
1161
+ gap: 12,
1162
+ paddingHorizontal: 16,
1163
+ paddingTop: 12,
1164
+ paddingBottom: 16,
1165
+ borderTopWidth: 1
1166
+ },
1167
+ queueClearBtn: {
1168
+ paddingVertical: 8,
1169
+ paddingHorizontal: 4
1170
+ },
1171
+ queueClearText: {
1172
+ fontSize: 14,
1173
+ fontWeight: '600'
1174
+ },
1175
+ queuePlayBtn: {
1176
+ flex: 1,
1177
+ flexDirection: 'row',
1178
+ alignItems: 'center',
1179
+ justifyContent: 'center',
1180
+ gap: 8,
1181
+ paddingVertical: 12,
1182
+ borderRadius: 12
1183
+ },
1184
+ queuePlayText: {
1185
+ color: '#FFFFFF',
1186
+ fontSize: 15,
1187
+ fontWeight: '700'
1188
+ }
1189
+ });
1190
+ //# sourceMappingURL=PodcastStreamPicker.js.map