@nyaruka/temba-components 0.42.1 → 0.43.1

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 (88) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/{dae3a0ce.js → 481989ca.js} +464 -444
  3. package/dist/index.js +464 -444
  4. package/dist/sw.js +1 -1
  5. package/dist/sw.js.map +1 -1
  6. package/dist/templates/components-body.html +1 -1
  7. package/dist/templates/components-head.html +1 -1
  8. package/out-tsc/src/compose/Compose.js +21 -19
  9. package/out-tsc/src/compose/Compose.js.map +1 -1
  10. package/out-tsc/src/contacts/ContactChat.js +3 -4
  11. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  12. package/out-tsc/src/contacts/ContactHistory.js +17 -1
  13. package/out-tsc/src/contacts/ContactHistory.js.map +1 -1
  14. package/out-tsc/src/contacts/events.js +64 -58
  15. package/out-tsc/src/contacts/events.js.map +1 -1
  16. package/out-tsc/src/lightbox/Lightbox.js +149 -0
  17. package/out-tsc/src/lightbox/Lightbox.js.map +1 -0
  18. package/out-tsc/src/list/ContentMenu.js +0 -4
  19. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  20. package/out-tsc/src/list/TembaMenu.js +2 -42
  21. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  22. package/out-tsc/src/list/TicketList.js +2 -2
  23. package/out-tsc/src/list/TicketList.js.map +1 -1
  24. package/out-tsc/src/utils/index.js +64 -2
  25. package/out-tsc/src/utils/index.js.map +1 -1
  26. package/out-tsc/src/vectoricon/index.js +1 -1
  27. package/out-tsc/src/vectoricon/index.js.map +1 -1
  28. package/out-tsc/temba-modules.js +2 -0
  29. package/out-tsc/temba-modules.js.map +1 -1
  30. package/out-tsc/test/temba-compose.test.js +5 -6
  31. package/out-tsc/test/temba-compose.test.js.map +1 -1
  32. package/out-tsc/test/temba-contact-chat.test.js +28 -14
  33. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  34. package/out-tsc/test/temba-contact-history.test.js +1 -1
  35. package/out-tsc/test/temba-contact-history.test.js.map +1 -1
  36. package/out-tsc/test/temba-lightbox.test.js +28 -0
  37. package/out-tsc/test/temba-lightbox.test.js.map +1 -0
  38. package/out-tsc/test/utils.test.js +1 -2
  39. package/out-tsc/test/utils.test.js.map +1 -1
  40. package/package.json +1 -1
  41. package/screenshots/truth/contacts/compose-attachments-no-text-failure.png +0 -0
  42. package/screenshots/truth/contacts/compose-attachments-no-text-success.png +0 -0
  43. package/screenshots/truth/contacts/compose-text-and-attachments-failure-attachments.png +0 -0
  44. package/screenshots/truth/contacts/compose-text-and-attachments-failure-generic.png +0 -0
  45. package/screenshots/truth/contacts/compose-text-and-attachments-failure-text.png +0 -0
  46. package/screenshots/truth/contacts/compose-text-and-attachments-success.png +0 -0
  47. package/screenshots/truth/contacts/compose-text-no-attachments-failure.png +0 -0
  48. package/screenshots/truth/contacts/compose-text-no-attachments-success.png +0 -0
  49. package/screenshots/truth/contacts/contact-active-default.png +0 -0
  50. package/screenshots/truth/contacts/contact-active-show-chatbox.png +0 -0
  51. package/screenshots/truth/contacts/contact-active-ticket-closed-show-reopen-button.png +0 -0
  52. package/screenshots/truth/contacts/contact-active-ticket-open-show-chatbox.png +0 -0
  53. package/screenshots/truth/contacts/contact-archived-hide-chatbox.png +0 -0
  54. package/screenshots/truth/contacts/contact-archived-ticket-closed-hide-chatbox.png +0 -0
  55. package/screenshots/truth/contacts/contact-blocked-hide-chatbox.png +0 -0
  56. package/screenshots/truth/contacts/contact-stopped-hide-chatbox.png +0 -0
  57. package/screenshots/truth/contacts/history.png +0 -0
  58. package/screenshots/truth/lightbox/img-zoomed.png +0 -0
  59. package/screenshots/truth/lightbox/img.png +0 -0
  60. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  61. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  62. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  63. package/screenshots/truth/menu/menu-root.png +0 -0
  64. package/screenshots/truth/menu/menu-submenu.png +0 -0
  65. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  66. package/screenshots/truth/menu/menu-tasks.png +0 -0
  67. package/src/compose/Compose.ts +25 -23
  68. package/src/contacts/ContactChat.ts +3 -4
  69. package/src/contacts/ContactHistory.ts +19 -1
  70. package/src/contacts/events.ts +79 -64
  71. package/src/lightbox/Lightbox.ts +165 -0
  72. package/src/list/ContentMenu.ts +0 -4
  73. package/src/list/TembaMenu.ts +2 -45
  74. package/src/list/TicketList.ts +2 -2
  75. package/src/untyped.d.ts +1 -0
  76. package/src/utils/index.ts +76 -3
  77. package/src/vectoricon/index.ts +1 -1
  78. package/static/css/temba-components.css +4 -1
  79. package/temba-modules.ts +2 -0
  80. package/test/temba-compose.test.ts +5 -7
  81. package/test/temba-contact-chat.test.ts +28 -15
  82. package/test/temba-contact-history.test.ts +1 -1
  83. package/test/temba-lightbox.test.ts +35 -0
  84. package/test/utils.test.ts +1 -2
  85. package/test-assets/img/meow.jpg +0 -0
  86. package/test-assets/style.css +1 -0
  87. package/web-test-runner.config.mjs +4 -0
  88. package/screenshots/truth/menu/menu-focus.png +0 -0
@@ -0,0 +1,28 @@
1
+ import { fixture, assert } from '@open-wc/testing';
2
+ import { Lightbox } from '../src/lightbox/Lightbox';
3
+ import { assertScreenshot } from './utils.test';
4
+ export const getHTML = () => {
5
+ return `<temba-lightbox animationTime="0"></temba-lightbox>`;
6
+ };
7
+ // let clock: any;
8
+ describe('temba-lightbox', () => {
9
+ it('can be created', async () => {
10
+ const lightbox = await fixture(getHTML());
11
+ const img = await fixture("<img style='width:100px;height:auto' src='./test-assets/img/meow.jpg'/>");
12
+ await assertScreenshot('lightbox/img', {
13
+ x: 28,
14
+ y: 28,
15
+ width: 1024,
16
+ height: 768,
17
+ });
18
+ assert.instanceOf(lightbox, Lightbox);
19
+ lightbox.showElement(img);
20
+ await assertScreenshot('lightbox/img-zoomed', {
21
+ x: 28,
22
+ y: 28,
23
+ width: 1024,
24
+ height: 768,
25
+ });
26
+ });
27
+ });
28
+ //# sourceMappingURL=temba-lightbox.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temba-lightbox.test.js","sourceRoot":"","sources":["../../test/temba-lightbox.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,MAAM,CAAC,MAAM,OAAO,GAAG,GAAG,EAAE;IAC1B,OAAO,qDAAqD,CAAC;AAC/D,CAAC,CAAC;AAEF,kBAAkB;AAElB,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,QAAQ,GAAa,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,MAAM,GAAG,GAAG,MAAM,OAAO,CACvB,yEAAyE,CAC1E,CAAC;QAEF,MAAM,gBAAgB,CAAC,cAAc,EAAE;YACrC,CAAC,EAAE,EAAE;YACL,CAAC,EAAE,EAAE;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,GAAG;SACZ,CAAC,CAAC;QAEH,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACtC,QAAQ,CAAC,WAAW,CAAC,GAAkB,CAAC,CAAC;QAEzC,MAAM,gBAAgB,CAAC,qBAAqB,EAAE;YAC5C,CAAC,EAAE,EAAE;YACL,CAAC,EAAE,EAAE;YACL,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,GAAG;SACZ,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { fixture, assert } from '@open-wc/testing';\nimport { Lightbox } from '../src/lightbox/Lightbox';\nimport { assertScreenshot } from './utils.test';\n\nexport const getHTML = () => {\n return `<temba-lightbox animationTime=\"0\"></temba-lightbox>`;\n};\n\n// let clock: any;\n\ndescribe('temba-lightbox', () => {\n it('can be created', async () => {\n const lightbox: Lightbox = await fixture(getHTML());\n const img = await fixture(\n \"<img style='width:100px;height:auto' src='./test-assets/img/meow.jpg'/>\"\n );\n\n await assertScreenshot('lightbox/img', {\n x: 28,\n y: 28,\n width: 1024,\n height: 768,\n });\n\n assert.instanceOf(lightbox, Lightbox);\n lightbox.showElement(img as HTMLElement);\n\n await assertScreenshot('lightbox/img-zoomed', {\n x: 28,\n y: 28,\n width: 1024,\n height: 768,\n });\n });\n});\n"]}
@@ -29,8 +29,7 @@ export const getComponent = async (tag, attrs = {}, slot = '', width = 250, heig
29
29
  ${style ? style : ``}
30
30
  `;
31
31
  parentNode.setAttribute('style', styleAttribute);
32
- const component = await fixture(spec, { parentNode });
33
- return component;
32
+ return await fixture(spec, { parentNode });
34
33
  };
35
34
  const createResponse = mocked => {
36
35
  const mockResponse = new window.Response(mocked.body, {
@@ -1 +1 @@
1
- {"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../test/utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import '../temba-modules';\nimport { DateTime } from 'luxon';\ninterface Clip {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nimport { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';\nimport MouseHelper from './MouseHelper';\nimport { Store } from '../src/store/Store';\nimport { replace, stub } from 'sinon';\n\nexport interface CodeMock {\n endpoint: RegExp;\n body: string;\n headers: any;\n status: string;\n}\n\nconst gets: CodeMock[] = [];\nlet posts: CodeMock[] = [];\nlet normalFetch;\n\nexport const showMouse = async () => {\n const mouse = await fixture(html`<mouse-helper />`);\n assert.instanceOf(mouse, MouseHelper);\n};\n\nexport const getAttributes = (attrs: any = {}) => {\n return `${Object.keys(attrs)\n .map((name: string) => {\n if (typeof attrs[name] === 'boolean' && attrs[name]) {\n return name;\n }\n return `${name}='${attrs[name]}'`;\n })\n .join(' ')}`;\n};\n\nexport const getComponent = async (\n tag,\n attrs: any = {},\n slot = '',\n width = 250,\n height = 0,\n style = ''\n) => {\n const spec = `<${tag} ${getAttributes(attrs)}>${slot}</${tag}>`;\n const parentNode = document.createElement('div');\n const styleAttribute = `\n ${width > 0 ? `width:${width}px;` : ``} \n ${height > 0 ? `height:${height}px;` : ``}\n ${style ? style : ``}\n `;\n parentNode.setAttribute('style', styleAttribute);\n const component = await fixture(spec, { parentNode });\n return component;\n};\n\nconst createResponse = mocked => {\n const mockResponse = new window.Response(mocked.body, {\n status: mocked.status,\n headers: {\n 'Content-type': 'text/html',\n ...mocked.headers,\n },\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst createJSONResponse = mocked => {\n const mockResponse = new window.Response(JSON.stringify(mocked.body), {\n status: mocked.status,\n headers: {\n 'Content-type': 'application/json',\n ...mocked.headers,\n },\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst getResponse = (endpoint: string, options) => {\n // check if our path has been mocked in code\n const mocks = options.method === 'GET' ? gets : posts;\n const codeMock = mocks.find(mock => mock.endpoint.test(endpoint));\n\n if (codeMock) {\n if (typeof codeMock.body === 'string') {\n // see if we are being mocked to a file\n if (codeMock.body.startsWith('/')) {\n endpoint = codeMock.body;\n } else {\n return createResponse(codeMock);\n }\n } else {\n return createJSONResponse(codeMock);\n }\n }\n\n // otherwise fetch over http\n return normalFetch(endpoint, options);\n};\n\nbefore(async () => {\n normalFetch = window.fetch;\n stub(window, 'fetch').callsFake(getResponse);\n await setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });\n});\n\nafter(() => {\n (window.fetch as any).restore();\n});\n\nexport const mockGET = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n gets.push({ endpoint, body, headers, status });\n};\n\nexport const mockPOST = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n posts.push({ endpoint, body, headers, status });\n};\n\nexport const clearMockPosts = () => {\n posts = [];\n};\n\nexport const checkTimers = (clock: any) => {\n expect(!!clock.timers).to.equal(true, 'Expected timers not found');\n expect(\n Object.keys(clock.timers).length,\n `Timers still to be run ${JSON.stringify(clock.timers)}`\n ).to.equal(0);\n};\n\nexport const delay = (millis: number) => {\n return new Promise(function (resolve) {\n window.setTimeout(resolve, millis);\n });\n};\n\nexport const assertScreenshot = async (\n filename: string,\n clip: Clip,\n waitFor?: { clock?: any; predicate?: () => boolean }\n) => {\n if (waitFor) {\n if (waitFor.clock) {\n waitFor.clock.restore();\n }\n await waitUntil(waitFor.predicate);\n }\n\n const threshold = 0.1;\n const exclude: Clip[] = [];\n\n try {\n await (window as any).matchPageSnapshot(\n `${filename}.png`,\n clip,\n exclude,\n threshold\n );\n } catch (error) {\n if (error.message) {\n throw new Error(\n `${error.message} ${\n error.expected\n ? `Expected ${error.expected} but got ${error.actual}`\n : ''\n } ${error.files ? `\\n${error.files.join('\\n')}` : ''}`\n );\n }\n throw new Error(error);\n }\n};\n\nexport const getClip = (ele: HTMLElement) => {\n let clip: any = ele.getBoundingClientRect();\n if (!clip.width || !clip.height) {\n clip = ele.shadowRoot.firstElementChild.getBoundingClientRect();\n }\n\n const padding = 10;\n const width = clip.width + padding * 2;\n const height = clip.height + padding * 2;\n const y = clip.y - padding;\n const x = clip.x - padding;\n\n const newClip = {\n x,\n y,\n width,\n height,\n bottom: y + height,\n right: x + width,\n top: y,\n left: x,\n };\n\n return newClip;\n};\n\nexport const getHTMLAttrs = (attrs: any = {}) => {\n return Object.keys(attrs)\n .map((name: string) => `${name}='${attrs[name]}'`)\n .join(' ');\n};\n\nexport const getHTML = (tag: string, attrs: any = {}) => {\n return `<${tag} ${getHTMLAttrs(attrs)}></${tag}>`;\n};\n\nexport const loadStore = async () => {\n const store: Store = await fixture(\n `<temba-store \n completion='/test-assets/store/editor.json'\n groups='/test-assets/store/groups.json'\n languages='/test-assets/store/languages.json'\n fields='/test-assets/store/fields.json'\n />`\n );\n await store.httpComplete;\n await store.httpComplete;\n\n return store;\n};\n\nexport const mockNow = (isodate: string) => {\n const now = DateTime.fromISO(isodate);\n // mock the current time\n replace(DateTime, 'now', () => {\n return now;\n });\n};\n"]}
1
+ {"version":3,"file":"utils.test.js","sourceRoot":"","sources":["../../test/utils.test.ts"],"names":[],"mappings":"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","sourcesContent":["import '../temba-modules';\nimport { DateTime } from 'luxon';\ninterface Clip {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nimport { expect, fixture, html, assert, waitUntil } from '@open-wc/testing';\nimport MouseHelper from './MouseHelper';\nimport { Store } from '../src/store/Store';\nimport { replace, stub } from 'sinon';\n\nexport interface CodeMock {\n endpoint: RegExp;\n body: string;\n headers: any;\n status: string;\n}\n\nconst gets: CodeMock[] = [];\nlet posts: CodeMock[] = [];\nlet normalFetch;\n\nexport const showMouse = async () => {\n const mouse = await fixture(html`<mouse-helper />`);\n assert.instanceOf(mouse, MouseHelper);\n};\n\nexport const getAttributes = (attrs: any = {}) => {\n return `${Object.keys(attrs)\n .map((name: string) => {\n if (typeof attrs[name] === 'boolean' && attrs[name]) {\n return name;\n }\n return `${name}='${attrs[name]}'`;\n })\n .join(' ')}`;\n};\n\nexport const getComponent = async (\n tag,\n attrs: any = {},\n slot = '',\n width = 250,\n height = 0,\n style = ''\n) => {\n const spec = `<${tag} ${getAttributes(attrs)}>${slot}</${tag}>`;\n const parentNode = document.createElement('div');\n const styleAttribute = `\n ${width > 0 ? `width:${width}px;` : ``} \n ${height > 0 ? `height:${height}px;` : ``}\n ${style ? style : ``}\n `;\n parentNode.setAttribute('style', styleAttribute);\n return await fixture(spec, { parentNode });\n};\n\nconst createResponse = mocked => {\n const mockResponse = new window.Response(mocked.body, {\n status: mocked.status,\n headers: {\n 'Content-type': 'text/html',\n ...mocked.headers,\n },\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst createJSONResponse = mocked => {\n const mockResponse = new window.Response(JSON.stringify(mocked.body), {\n status: mocked.status,\n headers: {\n 'Content-type': 'application/json',\n ...mocked.headers,\n },\n });\n\n return Promise.resolve(mockResponse);\n};\n\nconst getResponse = (endpoint: string, options) => {\n // check if our path has been mocked in code\n const mocks = options.method === 'GET' ? gets : posts;\n const codeMock = mocks.find(mock => mock.endpoint.test(endpoint));\n\n if (codeMock) {\n if (typeof codeMock.body === 'string') {\n // see if we are being mocked to a file\n if (codeMock.body.startsWith('/')) {\n endpoint = codeMock.body;\n } else {\n return createResponse(codeMock);\n }\n } else {\n return createJSONResponse(codeMock);\n }\n }\n\n // otherwise fetch over http\n return normalFetch(endpoint, options);\n};\n\nbefore(async () => {\n normalFetch = window.fetch;\n stub(window, 'fetch').callsFake(getResponse);\n await setViewport({ width: 1024, height: 768, deviceScaleFactor: 2 });\n});\n\nafter(() => {\n (window.fetch as any).restore();\n});\n\nexport const mockGET = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n gets.push({ endpoint, body, headers, status });\n};\n\nexport const mockPOST = (\n endpoint: RegExp,\n body: any,\n headers: any = {},\n status = '200'\n) => {\n posts.push({ endpoint, body, headers, status });\n};\n\nexport const clearMockPosts = () => {\n posts = [];\n};\n\nexport const checkTimers = (clock: any) => {\n expect(!!clock.timers).to.equal(true, 'Expected timers not found');\n expect(\n Object.keys(clock.timers).length,\n `Timers still to be run ${JSON.stringify(clock.timers)}`\n ).to.equal(0);\n};\n\nexport const delay = (millis: number) => {\n return new Promise(function (resolve) {\n window.setTimeout(resolve, millis);\n });\n};\n\nexport const assertScreenshot = async (\n filename: string,\n clip: Clip,\n waitFor?: { clock?: any; predicate?: () => boolean }\n) => {\n if (waitFor) {\n if (waitFor.clock) {\n waitFor.clock.restore();\n }\n await waitUntil(waitFor.predicate);\n }\n\n const threshold = 0.1;\n const exclude: Clip[] = [];\n\n try {\n await (window as any).matchPageSnapshot(\n `${filename}.png`,\n clip,\n exclude,\n threshold\n );\n } catch (error) {\n if (error.message) {\n throw new Error(\n `${error.message} ${\n error.expected\n ? `Expected ${error.expected} but got ${error.actual}`\n : ''\n } ${error.files ? `\\n${error.files.join('\\n')}` : ''}`\n );\n }\n throw new Error(error);\n }\n};\n\nexport const getClip = (ele: HTMLElement) => {\n let clip: any = ele.getBoundingClientRect();\n if (!clip.width || !clip.height) {\n clip = ele.shadowRoot.firstElementChild.getBoundingClientRect();\n }\n\n const padding = 10;\n const width = clip.width + padding * 2;\n const height = clip.height + padding * 2;\n const y = clip.y - padding;\n const x = clip.x - padding;\n\n const newClip = {\n x,\n y,\n width,\n height,\n bottom: y + height,\n right: x + width,\n top: y,\n left: x,\n };\n\n return newClip;\n};\n\nexport const getHTMLAttrs = (attrs: any = {}) => {\n return Object.keys(attrs)\n .map((name: string) => `${name}='${attrs[name]}'`)\n .join(' ');\n};\n\nexport const getHTML = (tag: string, attrs: any = {}) => {\n return `<${tag} ${getHTMLAttrs(attrs)}></${tag}>`;\n};\n\nexport const loadStore = async () => {\n const store: Store = await fixture(\n `<temba-store \n completion='/test-assets/store/editor.json'\n groups='/test-assets/store/groups.json'\n languages='/test-assets/store/languages.json'\n fields='/test-assets/store/fields.json'\n />`\n );\n await store.httpComplete;\n await store.httpComplete;\n\n return store;\n};\n\nexport const mockNow = (isodate: string) => {\n const now = DateTime.fromISO(isodate);\n // mock the current time\n replace(DateTime, 'now', () => {\n return now;\n });\n};\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.42.1",
3
+ "version": "0.43.1",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -16,13 +16,14 @@ import { Completion } from '../completion/Completion';
16
16
  export interface Attachment {
17
17
  uuid: string;
18
18
  content_type: string;
19
- type: string; //deprecated
20
19
  url: string;
21
- name: string;
20
+ filename: string;
22
21
  size: number;
23
22
  error: string;
24
23
  }
25
24
 
25
+ export const upload_endpoint = '/api/v2/media.json';
26
+
26
27
  export class Compose extends FormElement {
27
28
  static get styles() {
28
29
  return css`
@@ -199,7 +200,7 @@ export class Compose extends FormElement {
199
200
  accept = ''; //e.g. ".xls,.xlsx"
200
201
 
201
202
  @property({ type: String, attribute: false })
202
- endpoint = '/msgmedia/upload/';
203
+ endpoint = upload_endpoint;
203
204
 
204
205
  @property({ type: Boolean, attribute: false })
205
206
  uploading: boolean;
@@ -334,19 +335,21 @@ export class Compose extends FormElement {
334
335
  payload.append('file', file);
335
336
  postFormData(url, payload)
336
337
  .then((response: WebResponse) => {
337
- if (response.json.error) {
338
- this.addErrorValue(file, response.json.error);
339
- } else {
340
- const attachment = response.json as Attachment;
341
- if (attachment) {
342
- this.addValue(attachment);
343
- this.fireCustomEvent(CustomEventType.AttachmentAdded, attachment);
344
- }
338
+ const attachment = response.json as Attachment;
339
+ if (attachment) {
340
+ this.addValue(attachment);
341
+ this.fireCustomEvent(CustomEventType.AttachmentAdded, attachment);
345
342
  }
346
343
  })
347
- .catch((error: string) => {
348
- console.log(error);
349
- this.addErrorValue(file, error);
344
+ .catch((error: WebResponse) => {
345
+ let fileError = '';
346
+ if (error.status === 400) {
347
+ fileError = error.json.file[0];
348
+ } else {
349
+ fileError = 'Server failure';
350
+ }
351
+ console.error(fileError);
352
+ this.addErrorValue(file, fileError);
350
353
  })
351
354
  .finally(() => {
352
355
  this.uploading = false;
@@ -357,12 +360,11 @@ export class Compose extends FormElement {
357
360
  const errorValue = {
358
361
  uuid: Math.random().toString(36).slice(2, 6),
359
362
  content_type: file.type,
360
- type: file.type,
361
- name: file.name,
363
+ filename: file.name,
362
364
  url: file.name,
363
365
  size: file.size,
364
366
  error: error,
365
- };
367
+ } as Attachment;
366
368
  this.errorValues.push(errorValue);
367
369
  this.requestUpdate('errorValues');
368
370
  }
@@ -492,13 +494,13 @@ export class Compose extends FormElement {
492
494
  </div>
493
495
  <div class="attachment-name">
494
496
  <span
495
- title="${attachment.name} (${formatFileSize(
497
+ title="${attachment.filename} (${formatFileSize(
496
498
  attachment.size,
497
499
  2
498
- )}) ${attachment.type}"
499
- >${truncate(attachment.name, 25)}
500
+ )}) ${attachment.content_type}"
501
+ >${truncate(attachment.filename, 25)}
500
502
  (${formatFileSize(attachment.size, 0)})
501
- ${formatFileType(attachment.type)}</span
503
+ ${formatFileType(attachment.content_type)}</span
502
504
  >
503
505
  </div>
504
506
  </div>`;
@@ -516,11 +518,11 @@ export class Compose extends FormElement {
516
518
  </div>
517
519
  <div class="attachment-name">
518
520
  <span
519
- title="${errorAttachment.name} (${formatFileSize(
521
+ title="${errorAttachment.filename} (${formatFileSize(
520
522
  0,
521
523
  0
522
524
  )}) - Attachment failed - ${errorAttachment.error}"
523
- >${truncate(errorAttachment.name, 25)}
525
+ >${truncate(errorAttachment.filename, 25)}
524
526
  (${formatFileSize(0, 0)}) - Attachment failed</span
525
527
  >
526
528
  </div>
@@ -267,7 +267,7 @@ export class ContactChat extends ContactStoreElement {
267
267
  const buttonName = evt.detail.name;
268
268
  if (buttonName === 'Send') {
269
269
  const payload = {
270
- contacts: [this.currentContact.uuid],
270
+ contact: this.currentContact.uuid,
271
271
  };
272
272
  const compose = evt.currentTarget as Compose;
273
273
  if (compose) {
@@ -277,8 +277,7 @@ export class ContactChat extends ContactStoreElement {
277
277
  }
278
278
  const attachments = compose.values.map(attachment => {
279
279
  const content_type = attachment.content_type;
280
- const url = new URL(attachment.url, document.baseURI).href;
281
- return content_type + ':' + url;
280
+ return content_type + ':' + attachment.url;
282
281
  });
283
282
  if (attachments.length > 0) {
284
283
  payload['attachments'] = attachments;
@@ -290,7 +289,7 @@ export class ContactChat extends ContactStoreElement {
290
289
 
291
290
  const genericError = buttonName + ' failed, please try again.';
292
291
 
293
- postJSON(`/api/v2/broadcasts.json`, payload)
292
+ postJSON(`/api/v2/messages.json`, payload)
294
293
  .then(response => {
295
294
  if (response.status < 400) {
296
295
  compose.reset();
@@ -55,6 +55,7 @@ import {
55
55
  MIN_CHAT_REFRESH,
56
56
  SCROLL_THRESHOLD,
57
57
  } from './helpers';
58
+ import { Lightbox } from '../lightbox/Lightbox';
58
59
 
59
60
  // when images load, make sure we are on the bottom of the scroll window if necessary
60
61
  export const loadHandler = function (event) {
@@ -128,6 +129,7 @@ export class ContactHistory extends RapidElement {
128
129
  flex-grow: 1;
129
130
  min-height: 0;
130
131
  padding-top: 3em;
132
+ padding-bottom: 1em;
131
133
  }
132
134
 
133
135
  temba-loading {
@@ -222,6 +224,10 @@ export class ContactHistory extends RapidElement {
222
224
  .sticky-bin .attn {
223
225
  color: var(--color-text);
224
226
  }
227
+
228
+ .attachment img {
229
+ cursor: pointer;
230
+ }
225
231
  `;
226
232
  }
227
233
 
@@ -756,7 +762,7 @@ export class ContactHistory extends RapidElement {
756
762
  return renderTicketOpened(ticketEvent, closeHandler, !this.ticket);
757
763
  }
758
764
  case Events.TICKET_NOTE_ADDED:
759
- return renderNoteCreated(event as TicketEvent, this.agent);
765
+ return renderNoteCreated(event as TicketEvent);
760
766
 
761
767
  case Events.TICKET_ASSIGNED:
762
768
  return renderTicketAssigned(event as TicketEvent);
@@ -851,12 +857,24 @@ export class ContactHistory extends RapidElement {
851
857
  return !this.ticketEvents[ticket.uuid];
852
858
  }
853
859
 
860
+ private handleEventClicked(event) {
861
+ const ele = event.target as HTMLDivElement;
862
+ if (ele.tagName == 'IMG') {
863
+ // if we have one, show in our lightbox
864
+ const lightbox = document.querySelector('temba-lightbox') as Lightbox;
865
+ if (lightbox) {
866
+ lightbox.showElement(ele);
867
+ }
868
+ }
869
+ }
870
+
854
871
  private renderEventContainer(event: ContactEvent) {
855
872
  const stickyId = this.getStickyId(event);
856
873
  const isSticky = !!stickyId;
857
874
 
858
875
  const renderedEvent = html`
859
876
  <div
877
+ @click=${this.handleEventClicked}
860
878
  class="${this.ticket
861
879
  ? 'active-ticket'
862
880
  : ''} event ${event.type} ${isSticky ? 'has-sticky' : ''}"
@@ -1,6 +1,13 @@
1
1
  import { css, html, TemplateResult } from 'lit';
2
2
  import { Msg, ObjectReference, User } from '../interfaces';
3
- import { getClasses, oxford, oxfordFn, oxfordNamed, timeSince } from '../utils';
3
+ import {
4
+ getClasses,
5
+ oxford,
6
+ oxfordFn,
7
+ oxfordNamed,
8
+ renderAvatar,
9
+ timeSince,
10
+ } from '../utils';
4
11
  import { Icon } from '../vectoricon';
5
12
  import { getDisplayName } from './helpers';
6
13
 
@@ -165,11 +172,20 @@ export const getEventStyles = () => {
165
172
  }
166
173
 
167
174
  .msg {
168
- padding: var(--event-padding);
169
- border-radius: 8px;
175
+ border-radius: calc(var(--curvature) * 2.5);
170
176
  border: 2px solid rgba(100, 100, 100, 0.1);
171
177
  max-width: 300px;
172
178
  word-break: break-word;
179
+ overflow: hidden;
180
+ }
181
+
182
+ .msg.attachments-1.no-message {
183
+ border: 2px solid transparent;
184
+ background-color: transparent !important;
185
+ }
186
+
187
+ .msg .text {
188
+ padding: var(--event-padding);
173
189
  }
174
190
 
175
191
  .event.msg_received .msg {
@@ -186,7 +202,13 @@ export const getEventStyles = () => {
186
202
  .event.msg_created .msg,
187
203
  .event.broadcast_created .msg,
188
204
  .event.ivr_created .msg {
189
- background: rgb(231, 243, 255);
205
+ background: var(--color-primary-dark);
206
+ color: white;
207
+ font-weight: 400;
208
+ }
209
+
210
+ .msg.automated {
211
+ background: var(--color-automated) !important;
190
212
  }
191
213
 
192
214
  .webhook_called {
@@ -439,7 +461,17 @@ export const getEventStyles = () => {
439
461
  }
440
462
 
441
463
  .attachments {
442
- margin-top: 1em;
464
+ display: flex;
465
+ flex-wrap: wrap;
466
+ margin: -0.2em;
467
+ }
468
+
469
+ .attachment {
470
+ flex: 1 0 45%;
471
+ border-top: 0.05em solid transparent;
472
+ border-left: 0.05em solid transparent;
473
+ margin-top: 0.05em;
474
+ margin-left: 0.05em;
443
475
  }
444
476
  `;
445
477
  };
@@ -636,36 +668,10 @@ export const getEventGroupType = (event: ContactEvent, ticket: string) => {
636
668
  return 'verbose';
637
669
  };
638
670
 
639
- export const renderAvatar = (user: User, agent = '') => {
640
- const current = user && user.email === agent;
641
- if (user.email === FLOW_USER_ID || !user || !user.first_name) {
642
- return html`<temba-tip text="Automated message" position="top"
643
- ><div class="avatar flow" style="margin-top:0.5em">
644
- <temba-icon size="1" name="${Icon.flow_user}" /></div
645
- ></temba-tip>`;
646
- } else {
647
- return html`<temba-tip
648
- text="${user.first_name + ' ' + user.last_name}"
649
- position="top"
650
- >
651
- <div
652
- class="avatar"
653
- style="
654
- border-radius: 9999px;
655
- display:flex;
656
- align-items:center;
657
- border: 2px solid rgba(0,0,0,.05);
658
- background: ${current ? 'var(--color-primary-dark)' : '#eee'};
659
- color: ${current ? '#fff' : '#999'} ;
660
- font-weight: 400;
661
- padding: 0.5em;
662
- line-height:1.2em;
663
- "
664
- >
665
- ${user.first_name[0] + user.last_name[0]}
666
- </div>
667
- </temba-tip>`;
668
- }
671
+ export const renderUserAvatar = (user: User) => {
672
+ return html`<div style="width:3.5em;font-size:0.8em">
673
+ ${renderAvatar({ user, position: 'left' })}
674
+ </div>`;
669
675
  };
670
676
 
671
677
  export const renderAttachment = (attachment: string): TemplateResult => {
@@ -676,10 +682,12 @@ export const renderAttachment = (attachment: string): TemplateResult => {
676
682
 
677
683
  let inner = null;
678
684
  if (mediaType === 'image') {
679
- inner = html`<div class="linked" onclick="goto(event)" href="${url}"><img src="${url}" style="width:100%;height:auto;display:block"></img></a>`;
685
+ inner = html`
686
+ <img src="${url}" style="height:auto;width:100%;display:block;" />
687
+ `;
680
688
  } else if (ext === 'pdf') {
681
689
  return html`<div
682
- style="width:100%;height:300px;border-radius:var(--curvature);box-shadow:0px 0px 12px 0px rgba(0,0,0,.1), 0px 0px 2px 0px rgba(0,0,0,.15);overflow:hidden"
690
+ style="width:100%;height:300px;border-radius:calc(var(--curvature) * 2.5);box-shadow:0px 0px 12px 0px rgba(0,0,0,.1), 0px 0px 2px 0px rgba(0,0,0,.15);overflow:hidden"
683
691
  ><embed src="${url}#view=Fit" type="application/pdf" frameBorder="0" scrolling="auto" height="100%" width="100%"></embed></div>`;
684
692
  } else if (mediaType === 'video') {
685
693
  return html`<video
@@ -724,11 +732,7 @@ export const renderAttachment = (attachment: string): TemplateResult => {
724
732
  </div>`;
725
733
  }
726
734
 
727
- return html`<div
728
- style="width:100%;max-width:300px;border-radius:var(--curvature); box-shadow:0px 0px 6px 0px rgba(0,0,0,.15);overflow:hidden"
729
- >
730
- ${inner}
731
- </div>`;
735
+ return html`<div style="">${inner}</div>`;
732
736
  };
733
737
 
734
738
  export const renderMsgEvent = (
@@ -741,6 +745,7 @@ export const renderMsgEvent = (
741
745
 
742
746
  // summary items which appear under the message bubble
743
747
  const summary: TemplateResult[] = [];
748
+
744
749
  if (event.logs_url) {
745
750
  summary.push(html` <div class="icon-link">
746
751
  <temba-icon
@@ -786,17 +791,32 @@ export const renderMsgEvent = (
786
791
 
787
792
  return html`<div style="display:flex;align-items:flex-start">
788
793
  <div style="display:flex;flex-direction:column">
789
- ${event.msg.text ? html`<div class="msg">${event.msg.text}</div>` : null}
790
- ${event.msg.attachments
791
- ? html`<div class="attachments">
792
- ${event.msg.attachments.map(attachment =>
793
- renderAttachment(attachment)
794
- )}
795
- </div> `
796
- : null}
794
+ <div
795
+ class="${event.msg.text ? '' : 'no-message'} attachments-${(
796
+ event.msg.attachments || []
797
+ ).length} ${getClasses({
798
+ msg: true,
799
+ automated: !isInbound && !event.msg.created_by,
800
+ })}"
801
+ >
802
+ ${event.msg.text
803
+ ? html` <div class="text">${event.msg.text}</div> `
804
+ : null}
805
+ ${event.msg.attachments
806
+ ? html`<div class="attachments">
807
+ ${event.msg.attachments.map(
808
+ attachment =>
809
+ html` <div class="attachment">
810
+ ${renderAttachment(attachment)}
811
+ </div>`
812
+ )}
813
+ </div> `
814
+ : null}
815
+ </div>
797
816
  ${!event.msg.text && !event.msg.attachments
798
817
  ? html`<div class="unsupported">Unsupported Message</div>`
799
818
  : null}
819
+
800
820
  <div
801
821
  class="msg-summary"
802
822
  style="flex-direction:row${isInbound ? '-reverse' : ''}"
@@ -807,12 +827,10 @@ export const renderMsgEvent = (
807
827
  </div>
808
828
 
809
829
  ${!isInbound
810
- ? html`<div style="margin-left:0.8em;margin-top:0.3em">
830
+ ? html`<div style="margin-left:0.8em;margin-top:0.3em;font-size:0.9em">
811
831
  ${event.msg.created_by
812
- ? html`<div style="font-size:0.8em">
813
- ${renderAvatar(event.msg.created_by, agent)}
814
- </div>`
815
- : renderAvatar({ email: FLOW_USER_ID })}
832
+ ? renderUserAvatar(event.msg.created_by)
833
+ : renderUserAvatar(null)}
816
834
  </div>`
817
835
  : null}
818
836
  </div>`;
@@ -934,10 +952,7 @@ export const renderLabelsAdded = (event: LabelsAddedEvent): TemplateResult => {
934
952
  `;
935
953
  };
936
954
 
937
- export const renderNoteCreated = (
938
- event: TicketEvent,
939
- agent: string
940
- ): TemplateResult => {
955
+ export const renderNoteCreated = (event: TicketEvent): TemplateResult => {
941
956
  return html`<div style="display:flex;align-items:flex-start">
942
957
  <div style="display:flex;flex-direction:column">
943
958
  <div class="description">${event.note}</div>
@@ -951,7 +966,7 @@ export const renderNoteCreated = (
951
966
  </div>
952
967
  </div>
953
968
  <div style="margin-left:0.8em;margin-top:0.3em;font-size:0.8em">
954
- ${renderAvatar(event.created_by, agent)}
969
+ ${renderUserAvatar(event.created_by)}
955
970
  </div>
956
971
  </div>`;
957
972
  };
@@ -1187,16 +1202,16 @@ export const renderCampaignFiredEvent = (
1187
1202
  Campaign
1188
1203
  <span
1189
1204
  class="linked"
1190
- onclick="goto(event)"
1191
- href="/campaign/read/${event.campaign.uuid}"
1205
+ onclick="goto(event, this)"
1206
+ href="/campaign/read/${event.campaign.uuid}/"
1192
1207
  >${event.campaign.name}</span
1193
1208
  >
1194
1209
  ${event.fired_result === 'S' ? 'skipped' : 'triggered'}
1195
1210
  <span
1196
1211
  class="linked"
1197
- onclick="goto(event)"
1212
+ onclick="goto(event, this)"
1198
1213
  href="/campaignevent/read/${event.campaign.uuid}/${event.campaign_event
1199
- .id}"
1214
+ .id}/"
1200
1215
  >
1201
1216
  ${event.campaign_event.offset_display}
1202
1217
  ${event.campaign_event.relative_to.name}</span