@nyaruka/temba-components 0.86.0 → 0.87.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 (285) hide show
  1. package/.eslintrc.js +3 -3
  2. package/.prettierrc +6 -0
  3. package/.storybook/main.js +3 -3
  4. package/.storybook/preview.js +2 -2
  5. package/CHANGELOG.md +14 -0
  6. package/CreateIncludesPlugin.js +2 -2
  7. package/demo/index.html +1 -0
  8. package/dist/locales/es.js +1 -1
  9. package/dist/locales/es.js.map +1 -1
  10. package/dist/locales/fr.js +1 -1
  11. package/dist/locales/fr.js.map +1 -1
  12. package/dist/locales/pt.js +1 -1
  13. package/dist/locales/pt.js.map +1 -1
  14. package/dist/temba-components.js +296 -277
  15. package/dist/temba-components.js.map +1 -1
  16. package/out-tsc/src/RapidElement.js +3 -3
  17. package/out-tsc/src/RapidElement.js.map +1 -1
  18. package/out-tsc/src/ResizeElement.js +2 -2
  19. package/out-tsc/src/ResizeElement.js.map +1 -1
  20. package/out-tsc/src/aliaseditor/AliasEditor.js +1 -1
  21. package/out-tsc/src/aliaseditor/AliasEditor.js.map +1 -1
  22. package/out-tsc/src/button/Button.js +1 -1
  23. package/out-tsc/src/button/Button.js.map +1 -1
  24. package/out-tsc/src/charcount/helpers.js +1 -1
  25. package/out-tsc/src/charcount/helpers.js.map +1 -1
  26. package/out-tsc/src/colorpicker/ColorPicker.js +4 -4
  27. package/out-tsc/src/colorpicker/ColorPicker.js.map +1 -1
  28. package/out-tsc/src/completion/Completion.js +2 -2
  29. package/out-tsc/src/completion/Completion.js.map +1 -1
  30. package/out-tsc/src/completion/ExcellentParser.js +1 -1
  31. package/out-tsc/src/completion/ExcellentParser.js.map +1 -1
  32. package/out-tsc/src/completion/helpers.js +8 -8
  33. package/out-tsc/src/completion/helpers.js.map +1 -1
  34. package/out-tsc/src/compose/Compose.js +14 -14
  35. package/out-tsc/src/compose/Compose.js.map +1 -1
  36. package/out-tsc/src/contacts/ContactBadges.js +2 -2
  37. package/out-tsc/src/contacts/ContactBadges.js.map +1 -1
  38. package/out-tsc/src/contacts/ContactChat.js +4 -4
  39. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  40. package/out-tsc/src/contacts/ContactDetails.js +4 -4
  41. package/out-tsc/src/contacts/ContactDetails.js.map +1 -1
  42. package/out-tsc/src/contacts/ContactFieldEditor.js +3 -3
  43. package/out-tsc/src/contacts/ContactFieldEditor.js.map +1 -1
  44. package/out-tsc/src/contacts/ContactFields.js +2 -2
  45. package/out-tsc/src/contacts/ContactFields.js.map +1 -1
  46. package/out-tsc/src/contacts/ContactHistory.js +21 -19
  47. package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
  48. package/out-tsc/src/contacts/ContactPending.js +4 -4
  49. package/out-tsc/src/contacts/ContactPending.js.map +1 -1
  50. package/out-tsc/src/contacts/ContactTickets.js +8 -8
  51. package/out-tsc/src/contacts/ContactTickets.js.map +1 -1
  52. package/out-tsc/src/contacts/events.js +2 -2
  53. package/out-tsc/src/contacts/events.js.map +1 -1
  54. package/out-tsc/src/contacts/helpers.js +2 -2
  55. package/out-tsc/src/contacts/helpers.js.map +1 -1
  56. package/out-tsc/src/contactsearch/ContactSearch.js +7 -7
  57. package/out-tsc/src/contactsearch/ContactSearch.js.map +1 -1
  58. package/out-tsc/src/date/TembaDate.js +1 -1
  59. package/out-tsc/src/date/TembaDate.js.map +1 -1
  60. package/out-tsc/src/datepicker/DatePicker.js +1 -1
  61. package/out-tsc/src/datepicker/DatePicker.js.map +1 -1
  62. package/out-tsc/src/dialog/Dialog.js +5 -5
  63. package/out-tsc/src/dialog/Dialog.js.map +1 -1
  64. package/out-tsc/src/dialog/Modax.js +8 -8
  65. package/out-tsc/src/dialog/Modax.js.map +1 -1
  66. package/out-tsc/src/dropdown/Dropdown.js +1 -1
  67. package/out-tsc/src/dropdown/Dropdown.js.map +1 -1
  68. package/out-tsc/src/fields/FieldManager.js +6 -6
  69. package/out-tsc/src/fields/FieldManager.js.map +1 -1
  70. package/out-tsc/src/imagepicker/ImagePicker.js +4 -4
  71. package/out-tsc/src/imagepicker/ImagePicker.js.map +1 -1
  72. package/out-tsc/src/interfaces.js.map +1 -1
  73. package/out-tsc/src/label/Label.js +1 -1
  74. package/out-tsc/src/label/Label.js.map +1 -1
  75. package/out-tsc/src/leafletmap/LeafletMap.js +6 -6
  76. package/out-tsc/src/leafletmap/LeafletMap.js.map +1 -1
  77. package/out-tsc/src/leafletmap/helpers.js +2 -2
  78. package/out-tsc/src/leafletmap/helpers.js.map +1 -1
  79. package/out-tsc/src/lightbox/Lightbox.js +2 -2
  80. package/out-tsc/src/lightbox/Lightbox.js.map +1 -1
  81. package/out-tsc/src/list/ContentMenu.js +8 -8
  82. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  83. package/out-tsc/src/list/NotificationList.js +7 -3
  84. package/out-tsc/src/list/NotificationList.js.map +1 -1
  85. package/out-tsc/src/list/RunList.js +1 -1
  86. package/out-tsc/src/list/RunList.js.map +1 -1
  87. package/out-tsc/src/list/SortableList.js +6 -6
  88. package/out-tsc/src/list/SortableList.js.map +1 -1
  89. package/out-tsc/src/list/TembaList.js +5 -5
  90. package/out-tsc/src/list/TembaList.js.map +1 -1
  91. package/out-tsc/src/list/TembaMenu.js +22 -22
  92. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  93. package/out-tsc/src/loading/Loading.js +1 -1
  94. package/out-tsc/src/loading/Loading.js.map +1 -1
  95. package/out-tsc/src/locales/es.js +1 -1
  96. package/out-tsc/src/locales/es.js.map +1 -1
  97. package/out-tsc/src/locales/fr.js +1 -1
  98. package/out-tsc/src/locales/fr.js.map +1 -1
  99. package/out-tsc/src/locales/pt.js +1 -1
  100. package/out-tsc/src/locales/pt.js.map +1 -1
  101. package/out-tsc/src/omnibox/Omnibox.js +1 -1
  102. package/out-tsc/src/omnibox/Omnibox.js.map +1 -1
  103. package/out-tsc/src/options/Options.js +9 -9
  104. package/out-tsc/src/options/Options.js.map +1 -1
  105. package/out-tsc/src/remote/Remote.js +1 -1
  106. package/out-tsc/src/remote/Remote.js.map +1 -1
  107. package/out-tsc/src/select/Select.js +18 -18
  108. package/out-tsc/src/select/Select.js.map +1 -1
  109. package/out-tsc/src/sms/gsmsplitter.js +8 -8
  110. package/out-tsc/src/sms/gsmsplitter.js.map +1 -1
  111. package/out-tsc/src/sms/gsmvalidator.js +1 -1
  112. package/out-tsc/src/sms/gsmvalidator.js.map +1 -1
  113. package/out-tsc/src/sms/index.js +2 -2
  114. package/out-tsc/src/sms/index.js.map +1 -1
  115. package/out-tsc/src/sms/unicodesplitter.js +8 -8
  116. package/out-tsc/src/sms/unicodesplitter.js.map +1 -1
  117. package/out-tsc/src/store/Store.js +10 -10
  118. package/out-tsc/src/store/Store.js.map +1 -1
  119. package/out-tsc/src/store/StoreElement.js +2 -2
  120. package/out-tsc/src/store/StoreElement.js.map +1 -1
  121. package/out-tsc/src/tabpane/TabPane.js +4 -4
  122. package/out-tsc/src/tabpane/TabPane.js.map +1 -1
  123. package/out-tsc/src/templates/TemplateEditor.js +9 -9
  124. package/out-tsc/src/templates/TemplateEditor.js.map +1 -1
  125. package/out-tsc/src/textinput/TextInput.js +1 -1
  126. package/out-tsc/src/textinput/TextInput.js.map +1 -1
  127. package/out-tsc/src/thumbnail/Thumbnail.js +5 -5
  128. package/out-tsc/src/thumbnail/Thumbnail.js.map +1 -1
  129. package/out-tsc/src/tip/Tip.js +3 -3
  130. package/out-tsc/src/tip/Tip.js.map +1 -1
  131. package/out-tsc/src/utils/index.js +21 -21
  132. package/out-tsc/src/utils/index.js.map +1 -1
  133. package/out-tsc/src/vectoricon/VectorIcon.js +2 -2
  134. package/out-tsc/src/vectoricon/VectorIcon.js.map +1 -1
  135. package/out-tsc/src/vectoricon/index.js +2 -0
  136. package/out-tsc/src/vectoricon/index.js.map +1 -1
  137. package/out-tsc/src/webchat/WebChat.js +234 -81
  138. package/out-tsc/src/webchat/WebChat.js.map +1 -1
  139. package/out-tsc/src/webchat/assets.js +2 -0
  140. package/out-tsc/src/webchat/assets.js.map +1 -0
  141. package/out-tsc/src/webchat/index.js.map +1 -1
  142. package/out-tsc/test/temba-alert.test.js +1 -1
  143. package/out-tsc/test/temba-alert.test.js.map +1 -1
  144. package/out-tsc/test/temba-checkbox.test.js.map +1 -1
  145. package/out-tsc/test/temba-color-picker.test.js +4 -4
  146. package/out-tsc/test/temba-color-picker.test.js.map +1 -1
  147. package/out-tsc/test/temba-compose.test.js +50 -54
  148. package/out-tsc/test/temba-compose.test.js.map +1 -1
  149. package/out-tsc/test/temba-contact-badges.test.js +2 -2
  150. package/out-tsc/test/temba-contact-badges.test.js.map +1 -1
  151. package/out-tsc/test/temba-contact-chat.test.js +25 -38
  152. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  153. package/out-tsc/test/temba-contact-details.test.js +2 -2
  154. package/out-tsc/test/temba-contact-details.test.js.map +1 -1
  155. package/out-tsc/test/temba-contact-fields.test.js +4 -4
  156. package/out-tsc/test/temba-contact-fields.test.js.map +1 -1
  157. package/out-tsc/test/temba-contact-history.test.js +3 -3
  158. package/out-tsc/test/temba-contact-history.test.js.map +1 -1
  159. package/out-tsc/test/temba-contact-search.test.js +7 -7
  160. package/out-tsc/test/temba-contact-search.test.js.map +1 -1
  161. package/out-tsc/test/temba-contact-tickets.test.js +3 -3
  162. package/out-tsc/test/temba-contact-tickets.test.js.map +1 -1
  163. package/out-tsc/test/temba-content-menu.test.js +7 -7
  164. package/out-tsc/test/temba-content-menu.test.js.map +1 -1
  165. package/out-tsc/test/temba-date.test.js +3 -3
  166. package/out-tsc/test/temba-date.test.js.map +1 -1
  167. package/out-tsc/test/temba-datepicker.test.js +1 -1
  168. package/out-tsc/test/temba-datepicker.test.js.map +1 -1
  169. package/out-tsc/test/temba-field-manager.test.js +1 -3
  170. package/out-tsc/test/temba-field-manager.test.js.map +1 -1
  171. package/out-tsc/test/temba-label.test.js +6 -6
  172. package/out-tsc/test/temba-label.test.js.map +1 -1
  173. package/out-tsc/test/temba-lightbox.test.js +2 -2
  174. package/out-tsc/test/temba-lightbox.test.js.map +1 -1
  175. package/out-tsc/test/temba-list.test.js +6 -6
  176. package/out-tsc/test/temba-list.test.js.map +1 -1
  177. package/out-tsc/test/temba-menu.test.js +4 -5
  178. package/out-tsc/test/temba-menu.test.js.map +1 -1
  179. package/out-tsc/test/temba-modax.test.js +3 -3
  180. package/out-tsc/test/temba-modax.test.js.map +1 -1
  181. package/out-tsc/test/temba-options.test.js +1 -1
  182. package/out-tsc/test/temba-options.test.js.map +1 -1
  183. package/out-tsc/test/temba-select.test.js +17 -17
  184. package/out-tsc/test/temba-select.test.js.map +1 -1
  185. package/out-tsc/test/temba-sortable-list.test.js +1 -1
  186. package/out-tsc/test/temba-sortable-list.test.js.map +1 -1
  187. package/out-tsc/test/temba-textinput.test.js +2 -2
  188. package/out-tsc/test/temba-textinput.test.js.map +1 -1
  189. package/out-tsc/test/temba-tip.test.js +4 -4
  190. package/out-tsc/test/temba-tip.test.js.map +1 -1
  191. package/out-tsc/test/utils.test.js +8 -8
  192. package/out-tsc/test/utils.test.js.map +1 -1
  193. package/package.json +6 -15
  194. package/src/RapidElement.ts +3 -3
  195. package/src/ResizeElement.ts +2 -2
  196. package/src/aliaseditor/AliasEditor.ts +1 -2
  197. package/src/button/Button.ts +1 -1
  198. package/src/charcount/helpers.ts +1 -1
  199. package/src/colorpicker/ColorPicker.ts +4 -4
  200. package/src/completion/Completion.ts +2 -2
  201. package/src/completion/ExcellentParser.ts +1 -1
  202. package/src/completion/helpers.ts +9 -9
  203. package/src/compose/Compose.ts +18 -16
  204. package/src/contacts/ContactBadges.ts +2 -2
  205. package/src/contacts/ContactChat.ts +4 -4
  206. package/src/contacts/ContactDetails.ts +4 -4
  207. package/src/contacts/ContactFieldEditor.ts +4 -4
  208. package/src/contacts/ContactFields.ts +2 -2
  209. package/src/contacts/ContactHistory.ts +25 -22
  210. package/src/contacts/ContactPending.ts +4 -4
  211. package/src/contacts/ContactTickets.ts +9 -9
  212. package/src/contacts/events.ts +3 -3
  213. package/src/contacts/helpers.ts +2 -2
  214. package/src/contactsearch/ContactSearch.ts +9 -9
  215. package/src/date/TembaDate.ts +1 -1
  216. package/src/datepicker/DatePicker.ts +1 -1
  217. package/src/dialog/Dialog.ts +6 -6
  218. package/src/dialog/Modax.ts +8 -8
  219. package/src/dropdown/Dropdown.ts +1 -2
  220. package/src/emojis.json +1882 -1
  221. package/src/fields/FieldManager.ts +6 -7
  222. package/src/imagepicker/ImagePicker.ts +4 -4
  223. package/src/interfaces.ts +4 -4
  224. package/src/label/Label.ts +1 -1
  225. package/src/leafletmap/LeafletMap.ts +6 -6
  226. package/src/leafletmap/helpers.ts +2 -2
  227. package/src/lightbox/Lightbox.ts +2 -2
  228. package/src/list/ContentMenu.ts +9 -9
  229. package/src/list/NotificationList.ts +7 -3
  230. package/src/list/RunList.ts +1 -1
  231. package/src/list/SortableList.ts +6 -6
  232. package/src/list/TembaList.ts +5 -5
  233. package/src/list/TembaMenu.ts +23 -23
  234. package/src/loading/Loading.ts +1 -1
  235. package/src/locales/es.ts +1 -1
  236. package/src/locales/fr.ts +1 -1
  237. package/src/locales/pt.ts +1 -1
  238. package/src/omnibox/Omnibox.ts +2 -2
  239. package/src/options/Options.ts +9 -9
  240. package/src/remote/Remote.ts +1 -1
  241. package/src/select/Select.ts +19 -19
  242. package/src/sms/gsmsplitter.ts +8 -8
  243. package/src/sms/gsmvalidator.ts +1 -1
  244. package/src/sms/index.ts +2 -2
  245. package/src/sms/unicodesplitter.ts +8 -8
  246. package/src/store/Store.ts +10 -10
  247. package/src/store/StoreElement.ts +2 -2
  248. package/src/tabpane/TabPane.ts +4 -4
  249. package/src/templates/TemplateEditor.ts +9 -9
  250. package/src/textinput/TextInput.ts +2 -2
  251. package/src/thumbnail/Thumbnail.ts +5 -5
  252. package/src/tip/Tip.ts +3 -3
  253. package/src/utils/index.ts +24 -24
  254. package/src/vectoricon/VectorIcon.ts +2 -2
  255. package/src/vectoricon/index.ts +3 -1
  256. package/src/webchat/WebChat.ts +272 -87
  257. package/src/webchat/assets.ts +2 -0
  258. package/src/webchat/index.ts +1 -1
  259. package/svg.js +28 -29
  260. package/test/temba-alert.test.ts +1 -1
  261. package/test/temba-checkbox.test.ts +1 -1
  262. package/test/temba-color-picker.test.ts +4 -4
  263. package/test/temba-compose.test.ts +50 -55
  264. package/test/temba-contact-badges.test.ts +2 -2
  265. package/test/temba-contact-chat.test.ts +26 -46
  266. package/test/temba-contact-details.test.ts +2 -8
  267. package/test/temba-contact-fields.test.ts +4 -11
  268. package/test/temba-contact-history.test.ts +3 -3
  269. package/test/temba-contact-search.test.ts +7 -13
  270. package/test/temba-contact-tickets.test.ts +3 -3
  271. package/test/temba-content-menu.test.ts +7 -7
  272. package/test/temba-date.test.ts +3 -3
  273. package/test/temba-datepicker.test.ts +1 -1
  274. package/test/temba-field-manager.test.ts +1 -4
  275. package/test/temba-label.test.ts +6 -6
  276. package/test/temba-lightbox.test.ts +2 -2
  277. package/test/temba-list.test.ts +6 -6
  278. package/test/temba-menu.test.ts +4 -5
  279. package/test/temba-modax.test.ts +3 -3
  280. package/test/temba-options.test.ts +1 -1
  281. package/test/temba-select.test.ts +17 -17
  282. package/test/temba-sortable-list.test.ts +1 -1
  283. package/test/temba-textinput.test.ts +2 -2
  284. package/test/temba-tip.test.ts +5 -5
  285. package/test/utils.test.ts +8 -9
@@ -1,38 +1,53 @@
1
1
  /* eslint-disable @typescript-eslint/no-this-alias */
2
2
  import { LitElement, TemplateResult, html, css, PropertyValueMap } from 'lit';
3
3
  import { property } from 'lit/decorators.js';
4
+ import { getCookie, setCookie } from '../utils';
5
+ import { DEFAULT_AVATAR } from './assets';
6
+
7
+ interface User {
8
+ avatar?: string;
9
+ email: string;
10
+ name: string;
11
+ }
4
12
 
5
13
  interface Message {
6
- text: string;
7
14
  type: string;
15
+ msg_id?: string;
16
+ text?: string;
8
17
  chat_id?: string;
9
18
  origin?: string;
10
- timestamp?: number;
19
+ time?: string;
20
+ before?: string;
21
+ history?: Message[];
22
+ timeAsDate?: Date;
23
+ user?: User;
11
24
  }
12
25
 
13
26
  enum ChatStatus {
14
27
  DISCONNECTED = 'disconnected',
15
28
  CONNECTING = 'connecting',
16
- CONNECTED = 'connected',
29
+ CONNECTED = 'connected'
17
30
  }
18
31
 
19
32
  // how long of a window to show time between batches
20
33
  const BATCH_TIME_WINDOW = 30 * 60 * 1000;
34
+ const SCROLL_FETCH_BUFFER = 0.05;
35
+ const MIN_FETCH_TIME = 250;
21
36
 
22
37
  const TIME_FORMAT = { hour: 'numeric', minute: '2-digit' } as any;
23
38
  const DAY_FORMAT = {
24
39
  weekday: undefined,
25
40
  year: 'numeric',
26
41
  month: 'short',
27
- day: 'numeric',
42
+ day: 'numeric'
28
43
  } as any;
29
44
  const VERBOSE_FORMAT = {
30
45
  weekday: undefined,
31
- year: 'numeric',
46
+ year: undefined,
32
47
  month: 'short',
33
48
  day: 'numeric',
34
49
  hour: 'numeric',
35
- minute: '2-digit',
50
+ minute: '2-digit'
36
51
  } as any;
37
52
 
38
53
  export class WebChat extends LitElement {
@@ -46,12 +61,11 @@ export class WebChat extends LitElement {
46
61
  --color-primary: hsla(208, 70%, 55%, 1);
47
62
  font-family: 'Roboto', 'Helvetica Neue', sans-serif;
48
63
  font-weight: 400;
49
- font-size: 1.1em;
50
64
  --toggle-speed: 80ms;
51
- position: absolute;
65
+ position: fixed;
52
66
  right: 0;
53
67
  bottom: 0;
54
- z-index: 1;
68
+ z-index: 10000;
55
69
  }
56
70
 
57
71
  .header {
@@ -60,6 +74,8 @@ export class WebChat extends LitElement {
60
74
  display: flex;
61
75
  align-items: center;
62
76
  width: 100%;
77
+ color: rgba(255, 255, 255, 0.8);
78
+ font-size: 0.8em;
63
79
  }
64
80
 
65
81
  .header slot {
@@ -138,7 +154,7 @@ export class WebChat extends LitElement {
138
154
  cursor: pointer;
139
155
  transition: box-shadow var(--toggle-speed) ease-out;
140
156
  position: absolute;
141
- bottom: 0.5em;
157
+ bottom: 1em;
142
158
  right: 1em;
143
159
  }
144
160
 
@@ -185,7 +201,6 @@ export class WebChat extends LitElement {
185
201
  }
186
202
 
187
203
  .chat {
188
- max-width: 50vw;
189
204
  width: 28rem;
190
205
  border-radius: var(--curvature);
191
206
  overflow: hidden;
@@ -201,7 +216,7 @@ export class WebChat extends LitElement {
201
216
  }
202
217
 
203
218
  .chat.open {
204
- bottom: 5em;
219
+ bottom: 6em;
205
220
  opacity: 1;
206
221
  transform: scale(1);
207
222
  pointer-events: initial;
@@ -218,6 +233,8 @@ export class WebChat extends LitElement {
218
233
  -webkit-overflow-scrolling: touch;
219
234
  overflow-scrolling: touch;
220
235
  padding: 1em 1em 0 1em;
236
+ display: flex;
237
+ flex-direction: column-reverse;
221
238
  }
222
239
 
223
240
  .messages:before {
@@ -231,7 +248,6 @@ export class WebChat extends LitElement {
231
248
  height: 10px;
232
249
  display: block;
233
250
  position: absolute;
234
- max-width: 50vw;
235
251
  width: 28rem;
236
252
  transition: opacity var(--toggle-speed) ease-out;
237
253
  }
@@ -248,7 +264,6 @@ export class WebChat extends LitElement {
248
264
  display: block;
249
265
  position: absolute;
250
266
  margin-top: -10px;
251
- max-width: 50vw;
252
267
  width: 28rem;
253
268
  margin-right: 5em;
254
269
  transition: opacity var(--toggle-speed) ease-out;
@@ -324,6 +339,16 @@ export class WebChat extends LitElement {
324
339
  .input:disabled {
325
340
  background: transparent !important;
326
341
  }
342
+
343
+ temba-loading {
344
+ justify-content: center;
345
+ margin: 0.5em auto;
346
+ margin-bottom: 2em;
347
+ }
348
+
349
+ temba-loading.hidden {
350
+ display: none;
351
+ }
327
352
  `;
328
353
  }
329
354
 
@@ -334,7 +359,7 @@ export class WebChat extends LitElement {
334
359
  urn: string;
335
360
 
336
361
  @property({ type: Array })
337
- messages: Message[][] = [];
362
+ messageGroups: string[][] = [];
338
363
 
339
364
  // is our socket connection established
340
365
  @property({ type: String })
@@ -344,6 +369,9 @@ export class WebChat extends LitElement {
344
369
  @property({ type: Boolean })
345
370
  open = false;
346
371
 
372
+ @property({ type: Boolean })
373
+ fetching = false;
374
+
347
375
  @property({ type: Boolean })
348
376
  hasPendingText = false;
349
377
 
@@ -353,10 +381,20 @@ export class WebChat extends LitElement {
353
381
  @property({ type: Boolean, attribute: false })
354
382
  hideBottomScroll = true;
355
383
 
384
+ @property({ type: Boolean, attribute: false })
385
+ blockHistoryFetching = false;
386
+
356
387
  @property({ type: String })
357
388
  host: string;
358
389
 
390
+ @property({ type: String })
391
+ activeUserAvatar: string;
392
+
393
+ private msgMap = new Map<string, Message>();
359
394
  private sock: WebSocket;
395
+ private newMessageCount = 0;
396
+ private oldestMessageDate: Date;
397
+ private fetchRequested: Date;
360
398
 
361
399
  public constructor() {
362
400
  super();
@@ -366,6 +404,11 @@ export class WebChat extends LitElement {
366
404
  this.openSocket();
367
405
  }
368
406
 
407
+ private sendSockMessage(message: Message) {
408
+ console.log('MO', message);
409
+ this.sock.send(JSON.stringify(message));
410
+ }
411
+
369
412
  private openSocket(): void {
370
413
  if (this.status !== ChatStatus.DISCONNECTED) {
371
414
  return;
@@ -373,7 +416,7 @@ export class WebChat extends LitElement {
373
416
 
374
417
  this.status = ChatStatus.CONNECTING;
375
418
  const webChat = this;
376
- let url = `ws://localhost:8070/connect/${this.channel}/`;
419
+ let url = `wss://localhost.textit.com/connect/${this.channel}/`;
377
420
  if (this.urn) {
378
421
  url = `${url}?chat_id=${this.urn}`;
379
422
  }
@@ -387,44 +430,152 @@ export class WebChat extends LitElement {
387
430
  this.sock.onopen = function (event: Event) {
388
431
  console.log('Socket opened', event);
389
432
  webChat.status = ChatStatus.CONNECTED;
390
- sock.send(JSON.stringify({ type: 'start_chat' }));
433
+ webChat.urn = getCookie('temba-chat-urn');
434
+ const startChat = { type: 'start_chat' };
435
+ if (webChat.urn) {
436
+ startChat['chat_id'] = webChat.urn;
437
+ }
438
+ webChat.sendSockMessage(startChat);
391
439
  };
440
+
392
441
  this.sock.onmessage = function (event: MessageEvent) {
393
- console.log(event);
394
442
  webChat.status = ChatStatus.CONNECTED;
395
443
  const msg = JSON.parse(event.data) as Message;
444
+ console.log('MT', msg);
445
+
396
446
  if (msg.type === 'chat_started') {
397
447
  if (webChat.urn !== msg.chat_id) {
398
- webChat.messages = [];
448
+ webChat.messageGroups = [];
399
449
  }
400
450
  webChat.urn = msg.chat_id;
401
- webChat.requestUpdate('messages');
451
+ setCookie('temba-chat-urn', msg.chat_id);
452
+ webChat.requestUpdate('messageGroups');
402
453
  } else if (msg.type === 'chat_resumed') {
454
+ webChat.oldestMessageDate = new Date(msg.time);
403
455
  webChat.urn = msg.chat_id;
404
- } else if (msg.type === 'msg_created') {
405
- msg['timestamp'] = new Date().getTime();
456
+ webChat.fetchPreviousMessages();
457
+ } else if (msg.type === 'msg_out') {
406
458
  webChat.addMessage(msg);
407
- webChat.requestUpdate('messages');
459
+ webChat.insertGroups(webChat.groupMessages([msg.msg_id]), true);
460
+ } else if (msg.type === 'history') {
461
+ webChat.handleHistoryResponse(msg);
408
462
  }
409
463
  };
410
464
  }
411
465
 
412
- private restoreFromLocal(): void {
413
- const data = JSON.parse(localStorage.getItem('temba-chat') || '{}');
414
- const urn = 'urn' in data ? data['urn'] : null;
415
- if (urn && !this.urn) {
416
- this.urn = urn;
417
- const messages = 'messages' in data ? data['messages'] : [];
418
- this.messages.push(...messages);
466
+ private isSameGroup(msg1: Message, msg2: Message): boolean {
467
+ if (msg1 && msg2) {
468
+ return (
469
+ msg1.origin === msg2.origin &&
470
+ msg1.user?.name === msg2.user?.name &&
471
+ Math.abs(msg1.timeAsDate.getTime() - msg2.timeAsDate.getTime()) <
472
+ BATCH_TIME_WINDOW
473
+ );
419
474
  }
475
+ return false;
420
476
  }
421
477
 
422
- private writeToLocal(): void {
423
- // console.log('Writing to localStorage..');
424
- if (this.urn) {
425
- const data = { urn: this.urn, messages: this.messages, version: 1 };
426
- localStorage.setItem('temba-chat', JSON.stringify(data));
478
+ private insertGroups(newGroups: string[][], append = false) {
479
+ newGroups.reverse();
480
+ for (const newGroup of newGroups) {
481
+ // see if our new group belongs to the most recent group
482
+ const group =
483
+ this.messageGroups[append ? 0 : this.messageGroups.length - 1];
484
+
485
+ if (group) {
486
+ const lastMsgId = group[group.length - 1];
487
+ const lastMsg = this.msgMap.get(lastMsgId);
488
+ const newMsg = this.msgMap.get(newGroup[0]);
489
+ // if our message belongs to the previous group, in we go
490
+ if (this.isSameGroup(lastMsg, newMsg)) {
491
+ group.push(...newGroup);
492
+ } else {
493
+ // otherwise, just add our entire group as a new one
494
+ if (append) {
495
+ this.messageGroups.splice(0, 0, newGroup);
496
+ } else {
497
+ this.messageGroups.push(newGroup);
498
+ }
499
+ }
500
+ } else {
501
+ if (append) {
502
+ this.messageGroups.splice(0, 0, newGroup);
503
+ } else {
504
+ this.messageGroups.push(newGroup);
505
+ }
506
+ }
507
+ }
508
+
509
+ this.requestUpdate('messageGroups');
510
+ }
511
+
512
+ private groupMessages(msgIds: string[]): string[][] {
513
+ // group our messages by origin and user
514
+ const groups = [];
515
+ let lastGroup = [];
516
+ let lastMsg = null;
517
+ for (const msgId of msgIds) {
518
+ const msg = this.msgMap.get(msgId);
519
+ if (!this.isSameGroup(msg, lastMsg)) {
520
+ lastGroup = [];
521
+ groups.push(lastGroup);
522
+ }
523
+ lastGroup.push(msgId);
524
+ lastMsg = msg;
427
525
  }
526
+ return groups;
527
+ }
528
+
529
+ private fetchPreviousMessages() {
530
+ if (!this.blockHistoryFetching) {
531
+ this.blockHistoryFetching = true;
532
+ this.fetching = true;
533
+
534
+ const getHistoryMsg = { type: 'get_history' };
535
+ if (this.oldestMessageDate) {
536
+ getHistoryMsg['before'] = this.oldestMessageDate.toISOString();
537
+ }
538
+
539
+ this.fetchRequested = new Date();
540
+ this.sendSockMessage(getHistoryMsg);
541
+ }
542
+ }
543
+
544
+ private handleHistoryResponse(msg: Message) {
545
+ const elapsed = new Date().getTime() - this.fetchRequested.getTime();
546
+ window.setTimeout(
547
+ () => {
548
+ this.fetching = false;
549
+ // block of historical messages
550
+ const msgs = msg.history.reverse();
551
+
552
+ // first add messages to the map
553
+ const newMessages = [];
554
+ for (const m of msgs) {
555
+ if (this.addMessage(m)) {
556
+ newMessages.push(m.msg_id);
557
+ }
558
+ }
559
+
560
+ if (newMessages.length === 0) {
561
+ return;
562
+ }
563
+
564
+ this.insertGroups(this.groupMessages(newMessages));
565
+
566
+ const ele = this.shadowRoot.querySelector('.scroll');
567
+ const prevTop = ele.scrollTop;
568
+
569
+ window.setTimeout(() => {
570
+ ele.scrollTop = prevTop;
571
+ this.blockHistoryFetching = false;
572
+ }, 100);
573
+ },
574
+ // if it's the first load don't wait, otherwise wait a minimum amount of time
575
+ this.messageGroups.length === 0
576
+ ? 0
577
+ : Math.max(0, MIN_FETCH_TIME - elapsed)
578
+ );
428
579
  }
429
580
 
430
581
  public firstUpdated(
@@ -462,30 +613,27 @@ export class WebChat extends LitElement {
462
613
  this.focusInput();
463
614
  }
464
615
  }
616
+ }
465
617
 
466
- if (changed.has('channel')) {
467
- this.restoreFromLocal();
618
+ private addMessage(msg: Message): boolean {
619
+ if (msg.time && !msg.timeAsDate) {
620
+ msg.timeAsDate = new Date(msg.time);
468
621
  }
469
622
 
470
- if (changed.has('messages')) {
471
- // console.log('messages changed', this.messages);
472
- this.writeToLocal();
473
- // console.log(this.messages);
474
- this.scrollToBottom();
623
+ if (
624
+ !this.oldestMessageDate ||
625
+ msg.timeAsDate.getTime() < this.oldestMessageDate.getTime()
626
+ ) {
627
+ this.oldestMessageDate = msg.timeAsDate;
475
628
  }
476
- }
477
629
 
478
- private addMessage(msg: Message) {
479
- let lastGroup =
480
- this.messages.length > 0 ? this.messages[this.messages.length - 1] : [];
481
- const isSame = lastGroup.length === 0 || lastGroup[0].origin === msg.origin;
482
- if (!isSame) {
483
- lastGroup = [];
484
- }
485
- if (lastGroup.length === 0) {
486
- this.messages.push(lastGroup);
630
+ const isNew = !this.msgMap.has(msg.msg_id);
631
+ this.msgMap.set(msg.msg_id, msg);
632
+
633
+ if (msg.user?.avatar) {
634
+ this.activeUserAvatar = msg.user.avatar;
487
635
  }
488
- lastGroup.push(msg);
636
+ return isNew;
489
637
  }
490
638
 
491
639
  public openChat(): void {
@@ -507,13 +655,15 @@ export class WebChat extends LitElement {
507
655
  input.value = '';
508
656
 
509
657
  const msg = {
658
+ msg_id: `pending-${this.newMessageCount++}`,
510
659
  type: 'send_msg',
511
660
  text: text,
661
+ time: new Date().toISOString()
512
662
  };
513
663
 
514
664
  this.addMessage(msg);
515
- this.sock.send(JSON.stringify(msg));
516
- this.requestUpdate('messages');
665
+ this.insertGroups(this.groupMessages([msg.msg_id]), true);
666
+ this.sendSockMessage(msg);
517
667
  this.hasPendingText = input.value.length > 0;
518
668
  }
519
669
  }
@@ -527,42 +677,52 @@ export class WebChat extends LitElement {
527
677
  }
528
678
 
529
679
  private renderMessageGroup(
530
- messages: Message[],
680
+ msgIds: string[],
531
681
  idx: number,
532
- groups: Message[][]
682
+ groups: string[][]
533
683
  ): TemplateResult {
534
- let lastBatchTime = null;
684
+ const today = new Date();
685
+ let prevMsg;
535
686
  if (idx > 0) {
536
687
  const lastGroup = groups[idx - 1];
537
688
  if (lastGroup && lastGroup.length > 0) {
538
- lastBatchTime = lastGroup[lastGroup.length - 1].timestamp;
689
+ prevMsg = this.msgMap.get(lastGroup[0]);
539
690
  }
540
691
  }
541
692
 
542
- const newBatchTime = messages[0].timestamp;
543
- const showTime = newBatchTime - lastBatchTime > BATCH_TIME_WINDOW;
544
-
693
+ const currentMsg = this.msgMap.get(msgIds[msgIds.length - 1]);
545
694
  let timeDisplay = null;
546
- if (showTime) {
547
- let lastTime = null;
548
- const newTime = new Date(newBatchTime);
549
- if (lastBatchTime) {
550
- lastTime = new Date(lastBatchTime);
551
- }
552
- const showDay = !lastTime || newTime.getDate() !== lastTime.getDate();
695
+ if (
696
+ prevMsg &&
697
+ !this.isSameGroup(prevMsg, currentMsg) &&
698
+ prevMsg.timeAsDate.getTime() - currentMsg.timeAsDate.getTime() >
699
+ BATCH_TIME_WINDOW
700
+ ) {
701
+ const showDay =
702
+ !prevMsg ||
703
+ prevMsg.timeAsDate.getDate() !== currentMsg.timeAsDate.getDate();
553
704
  if (showDay) {
554
705
  timeDisplay = html`<div class="time">
555
- ${newTime.toLocaleDateString(undefined, DAY_FORMAT)}
706
+ ${prevMsg.timeAsDate.toLocaleDateString(undefined, DAY_FORMAT)}
556
707
  </div>`;
557
708
  } else {
558
- timeDisplay = html`<div class="time">
559
- ${newTime.toLocaleTimeString(undefined, TIME_FORMAT)}
560
- </div>`;
709
+ if (prevMsg.timeAsDate.getDate() !== today.getDate()) {
710
+ timeDisplay = html`<div class="time">
711
+ ${prevMsg.timeAsDate.toLocaleTimeString(undefined, VERBOSE_FORMAT)}
712
+ </div>`;
713
+ } else {
714
+ timeDisplay = html`<div class="time">
715
+ ${prevMsg.timeAsDate.toLocaleTimeString(undefined, TIME_FORMAT)}
716
+ </div>`;
717
+ }
561
718
  }
562
719
  }
563
720
 
564
- const blockTime = new Date(messages[messages.length - 1].timestamp);
565
- const incoming = !messages[0].origin;
721
+ const blockTime = new Date(this.msgMap.get(msgIds[msgIds.length - 1]).time);
722
+ const message = this.msgMap.get(msgIds[0]);
723
+ const incoming = !message.origin;
724
+ const avatar = message.user?.avatar;
725
+ const name = message.user?.name;
566
726
 
567
727
  return html` <div
568
728
  class="block ${incoming ? 'incoming' : 'outgoing'} ${idx === 0
@@ -570,30 +730,50 @@ export class WebChat extends LitElement {
570
730
  : ''}"
571
731
  title="${blockTime.toLocaleTimeString(undefined, VERBOSE_FORMAT)}"
572
732
  >
573
- ${timeDisplay}
574
733
  <div class="row">
575
734
  ${!incoming
576
735
  ? html`
577
736
  <div
578
737
  class="avatar"
579
- style="background: center / contain no-repeat url(https://dl-textit.s3.amazonaws.com/orgs/6418/media/5e81/5e814c83-bf33-43ea-b6c1-ee46f8acaf34/avatar.jpg)"
738
+ style="background: center / contain no-repeat url(${avatar ||
739
+ DEFAULT_AVATAR})"
580
740
  ></div>
581
741
  `
582
742
  : null}
583
743
 
584
744
  <div class="bubble">
585
- ${!incoming ? html`<div class="name">Henry McHelper</div>` : null}
586
- ${messages.map(msg => html`<div class="message">${msg.text}</div>`)}
745
+ ${!incoming ? html`<div class="name">${name}</div>` : null}
746
+ ${msgIds.map(
747
+ (msgId) =>
748
+ html`<div class="message">${this.msgMap.get(msgId).text}</div>
749
+ <!--div style="font-size:10px">
750
+ ${this.msgMap
751
+ .get(msgId)
752
+ .timeAsDate.toLocaleDateString(undefined, VERBOSE_FORMAT)}
753
+ </div-->`
754
+ )}
587
755
  </div>
588
756
  </div>
757
+ ${timeDisplay}
589
758
  </div>`;
590
759
  }
591
760
 
592
761
  private handleScroll(event: any) {
593
- this.hideBottomScroll =
594
- Math.round(event.target.scrollTop + event.target.clientHeight) >=
595
- event.target.scrollHeight;
596
- this.hideTopScroll = event.target.scrollTop === 0;
762
+ const ele = event.target;
763
+ const top = ele.scrollHeight - ele.clientHeight;
764
+ const scroll = Math.round(top + ele.scrollTop);
765
+ const scrollPct = scroll / top;
766
+
767
+ this.hideTopScroll = scrollPct <= 0.01;
768
+ this.hideBottomScroll = scrollPct >= 0.99;
769
+
770
+ if (this.blockHistoryFetching) {
771
+ return;
772
+ }
773
+
774
+ if (scrollPct < SCROLL_FETCH_BUFFER) {
775
+ this.fetchPreviousMessages();
776
+ }
597
777
  }
598
778
 
599
779
  private handleClickInputPanel(event: MouseEvent) {
@@ -617,7 +797,7 @@ export class WebChat extends LitElement {
617
797
  : ''}"
618
798
  >
619
799
  <div class="header">
620
- <slot name="header"></slot>
800
+ <slot name="header">${this.urn ? this.urn : 'Chat'}</slot>
621
801
  <temba-icon
622
802
  name="close"
623
803
  size="1.3"
@@ -627,12 +807,16 @@ export class WebChat extends LitElement {
627
807
  </div>
628
808
  <div class="messages">
629
809
  <div class="scroll" @scroll=${this.handleScroll}>
630
- ${this.messages
631
- ? this.messages.map(
810
+ ${this.messageGroups
811
+ ? this.messageGroups.map(
632
812
  (msgGroup, idx, groups) =>
633
813
  html`${this.renderMessageGroup(msgGroup, idx, groups)}`
634
814
  )
635
815
  : null}
816
+
817
+ <temba-loading
818
+ class="${!this.fetching ? 'hidden' : ''}"
819
+ ></temba-loading>
636
820
  </div>
637
821
  </div>
638
822
 
@@ -680,7 +864,8 @@ export class WebChat extends LitElement {
680
864
  <div @click=${this.toggleChat}>
681
865
  <div
682
866
  class="toggle"
683
- style="background: center / contain no-repeat url(https://dl-textit.s3.amazonaws.com/orgs/6418/media/5e81/5e814c83-bf33-43ea-b6c1-ee46f8acaf34/avatar.jpg)"
867
+ style="background: center / contain no-repeat url(${this
868
+ .activeUserAvatar || DEFAULT_AVATAR})"
684
869
  ></div>
685
870
  </div>
686
871
  `;
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_AVATAR =
2
+ 'data:image/png;base64,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';
@@ -2,5 +2,5 @@ export const SVG_FINGERPRINT = 'febafb41c2fd60efa2bdaead993c7087';
2
2
 
3
3
  // webchat spritesheet
4
4
  export enum WebChatIcon {
5
- send = 'send-03',
5
+ send = 'send-03'
6
6
  }