├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .jscsrc ├── .npmignore ├── .prettierrc ├── .template-lintrc.js ├── .travis.yml ├── .vscode └── settings.json ├── .watchmanconfig ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── addon ├── .gitkeep ├── components │ └── intercom-io.js ├── initializers │ └── ember-intercom.js ├── mixins │ └── intercom-route.js ├── services │ └── intercom.js └── templates │ └── components │ └── intercom-io.hbs ├── app ├── .gitkeep ├── components │ └── intercom-io.js ├── initializers │ └── ember-intercom.js └── services │ └── intercom.js ├── blueprints └── ember-intercom-io │ ├── files │ └── config │ │ └── environment.js │ └── index.js ├── client └── intercom-shim.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── ember-intercom.sublime-project ├── index.js ├── jsconfig.json ├── package.json ├── renovate.json ├── testem.js ├── tests ├── .eslintrc.js ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ └── .gitkeep │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── components │ │ │ └── .gitkeep │ │ │ ├── help.hbs │ │ │ └── index.hbs │ ├── config │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ ├── crossdomain.xml │ │ └── robots.txt ├── helpers │ ├── .gitkeep │ └── resolver.js ├── index.html ├── integration │ └── components │ │ └── intercom-io-test.js ├── test-helper.js └── unit │ ├── .gitkeep │ ├── initializers │ └── ember-intercom-test.js │ └── services │ └── intercom-test.js ├── vendor └── .gitkeep └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | 12 | # misc 13 | /coverage/ 14 | !.* 15 | 16 | # ember-try 17 | /.node_modules.ember-try/ 18 | /bower.json.ember-try 19 | /package.json.ember-try 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | root: true, 4 | parserOptions: { 5 | ecmaVersion: 2017, 6 | sourceType: 'module' 7 | }, 8 | plugins: ['ember'], 9 | extends: ['eslint:recommended', 'plugin:ember/recommended'], 10 | env: { 11 | browser: true 12 | }, 13 | rules: {}, 14 | overrides: [ 15 | // node files 16 | { 17 | files: [ 18 | '.template-lintrc.js', 19 | 'ember-cli-build.js', 20 | 'index.js', 21 | 'testem.js', 22 | 'blueprints/*/index.js', 23 | 'config/**/*.js', 24 | 'tests/dummy/config/**/*.js' 25 | ], 26 | excludedFiles: ['addon/**', 'addon-test-support/**', 'app/**', 'tests/dummy/app/**'], 27 | parserOptions: { 28 | sourceType: 'script', 29 | ecmaVersion: 2015 30 | }, 31 | env: { 32 | browser: false, 33 | node: true 34 | }, 35 | plugins: ['node'], 36 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { 37 | // add your custom rules and overrides for node files here 38 | }) 39 | } 40 | ] 41 | }; 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.sass-cache 13 | /connect.lock 14 | /coverage/ 15 | /libpeerconnection.log 16 | /npm-debug.log* 17 | /testem.log 18 | /yarn-error.log 19 | 20 | # ember-try 21 | /.node_modules.ember-try/ 22 | /bower.json.ember-try 23 | /package.json.ember-try 24 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "preset": "ember-suave", 3 | "excludeFiles": [ 4 | "./client/intercom-shim.js" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.eslintignore 13 | /.eslintrc.js 14 | /.gitignore 15 | /.template-lintrc.js 16 | /.travis.yml 17 | /.watchmanconfig 18 | /bower.json 19 | /config/ember-try.js 20 | /ember-cli-build.js 21 | /testem.js 22 | /tests/ 23 | /yarn.lock 24 | .gitkeep 25 | 26 | # ember-try 27 | /.node_modules.ember-try/ 28 | /bower.json.ember-try 29 | /package.json.ember-try 30 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "bracketSpacing": true, 4 | "trailingComma": "none", 5 | "useTabs": false, 6 | "tabWidth": 2, 7 | "printWidth": 120, 8 | "semi": true 9 | } 10 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended' 5 | }; 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | # we recommend testing addons with the same minimum supported node version as Ember CLI 4 | # so that your addon works for all apps 5 | - '8' 6 | 7 | sudo: false 8 | dist: trusty 9 | 10 | addons: 11 | chrome: stable 12 | 13 | cache: 14 | yarn: true 15 | yarn: true 16 | env: 17 | global: 18 | # See https://git.io/vdao3 for details. 19 | - JOBS=1 20 | 21 | stages: 22 | - 'Tests' 23 | - 'Additional Tests' 24 | - 'Canary Tests' 25 | - name: 'Deploy' 26 | if: branch = master AND type = push 27 | 28 | jobs: 29 | fail_fast: true 30 | allow_failures: 31 | - env: EMBER_TRY_SCENARIO=ember-canary 32 | 33 | include: 34 | # runs linting and tests with current locked deps 35 | 36 | - stage: 'Tests' 37 | name: 'Tests' 38 | script: 39 | - yarn lint:hbs 40 | - yarn lint:js 41 | - ember test 42 | 43 | # we recommend new addons test the current and previous LTS 44 | # as well as latest stable release (bonus points to beta/canary) 45 | - stage: 'Additional Tests' 46 | node_js: '10' 47 | env: EMBER_TRY_SCENARIO=ember-lts-2.16 48 | - env: EMBER_TRY_SCENARIO=ember-lts-2.18 49 | - env: EMBER_TRY_SCENARIO=ember-release 50 | - env: EMBER_TRY_SCENARIO=ember-beta 51 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 52 | 53 | - stage: 'Canary Tests' 54 | script: 55 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 56 | env: EMBER_TRY_SCENARIO=ember-canary 57 | 58 | - stage: 'Deploy' 59 | name: 'Publish to npm' 60 | install: 61 | - yarn install --non-interactive 62 | script: yarn semantic-release 63 | 64 | before_install: 65 | - curl -o- -L https://yarnpkg.com/install.sh | bash 66 | - export PATH=$HOME/.yarn/bin:$PATH 67 | 68 | install: 69 | - yarn install --no-lockfile --non-interactive 70 | 71 | script: 72 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 73 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "javascript.implicitProjectConfig.checkJs": true 3 | } -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [1.3.0](https://github.com/mike-north/ember-intercom-io/compare/v1.2.1...v1.3.0) (2019-10-25) 2 | 3 | 4 | ### Features 5 | 6 | * add support for `startTour` method ([a4b4c6e](https://github.com/mike-north/ember-intercom-io/commit/a4b4c6e76aa2d5aec0e5ddf71ab7e273183e3177)) 7 | 8 | ## [1.2.1](https://github.com/mike-north/ember-intercom-io/compare/v1.2.0...v1.2.1) (2019-09-09) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * user name existence as a requirement for valid user context ([9448e28](https://github.com/mike-north/ember-intercom-io/commit/9448e28)) 14 | 15 | # [1.2.0](https://github.com/mike-north/ember-intercom-io/compare/v1.1.5...v1.2.0) (2019-01-23) 16 | 17 | 18 | ### Features 19 | 20 | * **deps:** plugin-proposal-object-rest-spread ([#200](https://github.com/mike-north/ember-intercom-io/issues/200)) ([f46bc88](https://github.com/mike-north/ember-intercom-io/commit/f46bc88)) 21 | 22 | ## [1.1.5](https://github.com/mike-north/ember-intercom-io/compare/v1.1.4...v1.1.5) (2018-11-30) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * update README ([7375be2](https://github.com/mike-north/ember-intercom-io/commit/7375be2)) 28 | 29 | ## [1.1.4](https://github.com/mike-north/ember-intercom-io/compare/v1.1.3...v1.1.4) (2018-11-30) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * update README ([7764506](https://github.com/mike-north/ember-intercom-io/commit/7764506)), closes [#175](https://github.com/mike-north/ember-intercom-io/issues/175) 35 | 36 | ## [1.1.3](https://github.com/mike-north/ember-intercom-io/compare/v1.1.2...v1.1.3) (2018-11-26) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **deps:** update dependency ember-sinon-qunit to v3 ([#161](https://github.com/mike-north/ember-intercom-io/issues/161)) ([bd414ba](https://github.com/mike-north/ember-intercom-io/commit/bd414ba)) 42 | 43 | ## [1.1.2](https://github.com/mike-north/ember-intercom-io/compare/v1.1.1...v1.1.2) (2018-11-25) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * upgrade dependencies ([7c5971c](https://github.com/mike-north/ember-intercom-io/commit/7c5971c)) 49 | 50 | ## [1.1.1](https://github.com/mike-north/ember-intercom-io/compare/v1.1.0...v1.1.1) (2018-11-25) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * no need for optionalDependencies ([4facff5](https://github.com/mike-north/ember-intercom-io/commit/4facff5)) 56 | 57 | # [1.1.0](https://github.com/mike-north/ember-intercom-io/compare/v1.0.1...v1.1.0) (2018-11-25) 58 | 59 | 60 | ### Features 61 | 62 | * Advanced API integration from PR 143 ([#172](https://github.com/mike-north/ember-intercom-io/issues/172)) ([fddf27d](https://github.com/mike-north/ember-intercom-io/commit/fddf27d)) 63 | 64 | ## [1.0.1](https://github.com/mike-north/ember-intercom-io/compare/v1.0.0...v1.0.1) (2018-11-12) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * CI race condition ([335fe86](https://github.com/mike-north/ember-intercom-io/commit/335fe86)) 70 | * oldest LTS runs node10 in CI ([1129565](https://github.com/mike-north/ember-intercom-io/commit/1129565)) 71 | * replace Ember.merge w/ Ember.assign ([b9006c0](https://github.com/mike-north/ember-intercom-io/commit/b9006c0)) 72 | 73 | # 1.0.0 (2018-11-10) 74 | 75 | 76 | ### Bug Fixes 77 | 78 | * allow appId to be set w/ env variable ([a0014b7](https://github.com/mike-north/ember-intercom-io/commit/a0014b7)) 79 | * allow appId to be set w/ env variable ([ec32b6d](https://github.com/mike-north/ember-intercom-io/commit/ec32b6d)) 80 | * semantic-release config ([3403a61](https://github.com/mike-north/ember-intercom-io/commit/3403a61)) 81 | * update to RFC-232 tests ([7c38c5e](https://github.com/mike-north/ember-intercom-io/commit/7c38c5e)) 82 | 83 | 84 | ### chore 85 | 86 | * drop support for Node.js 0.10 ([e0c5f1e](https://github.com/mike-north/ember-intercom-io/commit/e0c5f1e)) 87 | 88 | 89 | ### Features 90 | 91 | * auto-release for non-PR commits on master ([eedbc62](https://github.com/mike-north/ember-intercom-io/commit/eedbc62)) 92 | * ember-cli upgrade ([20c782b](https://github.com/mike-north/ember-intercom-io/commit/20c782b)) 93 | 94 | 95 | ### BREAKING CHANGES 96 | 97 | * This module no longer supports Node.js 0.10 98 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Levanto Financial, 2016-18 Mike Works, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ember-intercom-io 2 | 3 | [![Build Status](https://travis-ci.org/mike-north/ember-intercom-io.svg?branch=master)](https://travis-ci.org/mike-north/ember-intercom-io) 4 | [![Code Climate](https://codeclimate.com/github/mike-north/ember-intercom-io/badges/gpa.svg)](https://codeclimate.com/github/mike-north/ember-intercom-io) 5 | [![Dependency Status](https://david-dm.org/mike-north/ember-intercom-io.svg)](https://david-dm.org/mike-north/ember-intercom-io) 6 | [![devDependency Status](https://david-dm.org/mike-north/ember-intercom-io/dev-status.svg)](https://david-dm.org/mike-north/ember-intercom-io#info=devDependencies) 7 | [![Ember Observer Score](http://emberobserver.com/badges/ember-intercom-io.svg)](http://emberobserver.com/addons/ember-intercom-io) 8 | 9 | [Intercom.io](http://intercom.io) integration for Ember.js apps. 10 | 11 | ## Setup 12 | 13 | **Install this addon with ember-cli** `ember install ember-intercom-io` 14 | 15 | **Add the `{{intercom-io}}` component to one of your templates`** 16 | The chat widget will appear whenever this component has been rendered, and should disappear whenever it's destroyed. 17 | 18 | ### Configuration 19 | 20 | **In your `config/environment.js` file, you must provide your `appId`** 21 | 22 | ```js 23 | 24 | module.exports = function(environment) { 25 | ... 26 | intercom: { 27 | appId: null, // <-- REPLACE WITH YOUR INTERCOM.IO app_id 28 | enabled: true, // <-- Setting to false in your testing environment prevents unneccessary network requests (true by default) 29 | userProperties: { 30 | createdAtProp: 'createdAt', 31 | emailProp: 'email', 32 | nameProp: 'name', 33 | userHashProp: 'hash', 34 | userIdProp: 'id' 35 | } 36 | }, 37 | ... 38 | }; 39 | 40 | ``` 41 | 42 | #### Users vs Leads 43 | 44 | In the [intercom.io](http://intercom.io) world, a lead is a visitor to your site or app, without an email or name associated with them. A user has a name and email, and is a good construct for tracking the history of all interactions w/ a single person. 45 | 46 | You can make `ember-intercom-io` aware of a "user" context (shifting into "users" mode instead of "leads" mode) by adding an object to the `intercom` service (i.e., your user authentication service). 47 | 48 | When the application updates the `intercom.user` object, changes will be sent to Intercom and reflected in your Intercom dashboard. 49 | 50 | **app/services/authentication.js** 51 | 52 | ```js 53 | import Service, {inject as service} from '@ember/service'; 54 | 55 | export default Service.extend({ 56 | intercom: service(), // the intercom service 57 | didLogin(user) { 58 | ... 59 | this.get('intercom').set('user.name', 'Joe Username'); 60 | this.get('intercom').set('user.email', 'joe@example.com'); 61 | this.get('intercom').set('user.createdAt', 1447135065173); 62 | } 63 | }); 64 | 65 | ``` 66 | 67 | #### Custom Properties 68 | 69 | To send custom properties on to intercom, add them to the `intercom.user` object. All property names will be underscored prior to being sent. 70 | undefined values will be removed (however, `null` is kept). 71 | 72 | ```js 73 | let customProperties = { 74 | myCustomThing: 1, 75 | numberOfCats: false, 76 | notDefined: undefined 77 | } 78 | set(this, 'intercom.user', customProperties); 79 | ``` 80 | 81 | becomes 82 | 83 | ```js 84 | { 85 | my_custom_thing: 1, 86 | number_of_cats: false 87 | } 88 | ``` 89 | 90 | ## API 91 | 92 | The `intercom` service exposes several public API methods that match Intercom.com's 93 | existing Javascript API. For full details on the client API, [read the Intercom docs.](https://developers.intercom.com/v2.0/docs/intercom-javascript#section-intercomonhide) 94 | 95 | ### Properties 96 | 97 | | Name | Type | 98 | ---| --- | 99 | | autoUpdate | Boolean | 100 | | hideDefaultLauncher | Boolean | 101 | | isOpen | Boolean | 102 | | isBooted | Boolean | 103 | | unreadCount | Integer | 104 | | user | Object | 105 | 106 | ### Methods 107 | 108 | The following intercom methods are implemented. See `services/intercom.js` for full 109 | details. 110 | 111 | `boot()` 112 | 113 | `update()` 114 | 115 | `shutdown()` 116 | 117 | `hide()` 118 | 119 | `show()` 120 | 121 | `showMessages()` 122 | 123 | `showNewMessage()` 124 | 125 | `trackEvent()` 126 | 127 | `getVisitorId()` Returns the current id of the logged in user. 128 | 129 | `startTour()` Your intercom account needs to support product tours 130 | 131 | ### Events 132 | 133 | Subscribe to events in your app with event listeners: 134 | 135 | ```js 136 | //fancy-component.js 137 | 138 | ... 139 | 140 | intercom: service(), 141 | newMessageAlert: on('intercom.unreadCountChange', function() { 142 | alert('Unread Count Changed!'); 143 | }), 144 | 145 | ... 146 | 147 | ``` 148 | 149 | **Available Events** 150 | 151 | (Read the Intercom documentation for full details)[https://developers.intercom.com/v2.0/docs/intercom-javascript#section-intercomonhide] 152 | 153 | | Ember Event | Intercom Event | 154 | --- | --- | 155 | | hide | `onHide` | 156 | | show | `onShow` | 157 | | unreadCountChange | `onUnreadCountChange` | 158 | 159 | 160 | ## Installation 161 | 162 | - `git clone` this repository 163 | - `npm install` 164 | 165 | ### Linting 166 | 167 | - `npm run lint:hbs` 168 | - `npm run lint:js` 169 | - `npm run lint:js -- --fix` 170 | 171 | ### Running tests 172 | 173 | - `ember test` – Runs the test suite on the current Ember version 174 | - `ember test --server` – Runs the test suite in "watch mode" 175 | - `ember try:each` – Runs the test suite against multiple Ember versions 176 | 177 | ### Running the dummy application 178 | 179 | - `ember serve` 180 | - Visit the dummy application at [http://localhost:4200](http://localhost:4200). 181 | 182 | For more information on using ember-cli, visit [http://www.ember-cli.com/](http://www.ember-cli.com/). 183 | 184 | Copyright (c) 2015 Levanto Financial, 2016-18 [Mike Works](https://mike.works), Inc. 185 | -------------------------------------------------------------------------------- /addon/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/addon/.gitkeep -------------------------------------------------------------------------------- /addon/components/intercom-io.js: -------------------------------------------------------------------------------- 1 | import { inject as service } from '@ember/service'; 2 | import Component from '@ember/component'; 3 | import { get } from '@ember/object'; 4 | 5 | export default Component.extend({ 6 | intercom: service(), 7 | didInsertElement() { 8 | this._super(...arguments); 9 | get(this, 'intercom').start(); 10 | }, 11 | willDestroyElement() { 12 | this._super(...arguments); 13 | get(this, 'intercom').stop(); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /addon/initializers/ember-intercom.js: -------------------------------------------------------------------------------- 1 | import { _setup } from 'intercom'; 2 | 3 | export function initialize(application) { 4 | if (typeof FastBoot === 'undefined') { 5 | let config; 6 | 7 | if (application.resolveRegistration) { 8 | config = application.resolveRegistration('config:environment'); 9 | } else { 10 | config = application.registry.resolve('config:environment'); 11 | } 12 | 13 | _setup(config); 14 | } 15 | } 16 | 17 | export default { 18 | name: 'ember-intercom', 19 | initialize 20 | }; 21 | -------------------------------------------------------------------------------- /addon/mixins/intercom-route.js: -------------------------------------------------------------------------------- 1 | import Mixin from '@ember/object/mixin'; 2 | import { inject as service } from '@ember/service'; 3 | import { on } from '@ember/object/evented'; 4 | import { get } from '@ember/object'; 5 | import { assert } from '@ember/debug'; 6 | 7 | export default Mixin.create({ 8 | intercom: service(), 9 | /** 10 | * push page changes to intercom 11 | * @private 12 | * @on didTransition 13 | */ 14 | submitRouteChange: on('didTransition', function() { 15 | let intercom = get(this, 'intercom'); 16 | 17 | assert('Could not find property configured intercom instance', intercom); 18 | 19 | if (get(intercom, 'isBooted')) { 20 | intercom.update(); 21 | } 22 | }) 23 | }); 24 | -------------------------------------------------------------------------------- /addon/services/intercom.js: -------------------------------------------------------------------------------- 1 | import { assign } from '@ember/polyfills'; 2 | import Service from '@ember/service'; 3 | import { computed, get, observer, set } from '@ember/object'; 4 | import { assert, warn } from '@ember/debug'; 5 | import intercom from 'intercom'; 6 | import { next } from '@ember/runloop'; 7 | import { typeOf } from '@ember/utils'; 8 | import { underscore } from '@ember/string'; 9 | import Evented from '@ember/object/evented'; 10 | import { alias } from '@ember/object/computed'; 11 | 12 | const WarnOption = { 13 | id: 'ember-intercom-io.missing-data' 14 | }; 15 | 16 | /** 17 | * Normalization function for converting intercom data to a consistent format. 18 | * 19 | * Changes: 20 | * - underscore keys 21 | * - convert dates to unix timestamps 22 | * 23 | * @param {Object} data 24 | * 25 | * @private 26 | * @return {Object} 27 | */ 28 | function normalizeIntercomMetadata(data) { 29 | let result = {}; 30 | let val; 31 | Object.keys(data).forEach(key => { 32 | val = data[key]; 33 | if (typeOf(val) === 'object') { 34 | result[underscore(key)] = normalizeIntercomMetadata(val); 35 | } else { 36 | if (typeOf(val) === 'date') { 37 | val = val.valueOf(); 38 | } 39 | if (typeOf(val) !== 'undefined') { 40 | result[underscore(key)] = val; 41 | } 42 | } 43 | }); 44 | 45 | return result; 46 | } 47 | 48 | export default Service.extend(Evented, { 49 | init() { 50 | this._super(...arguments); 51 | set(this, 'user', { email: null, name: null, hash: null, user_id: null }); 52 | }, 53 | 54 | api: intercom, 55 | user: null, 56 | /** 57 | * [description] 58 | * @return {[type]} [description] 59 | */ 60 | _userHashProp: computed('user', 'config.userProperties.userHashProp', function() { 61 | return get(this, `user.${get(this, 'config.userProperties.userHashProp')}`); 62 | }), 63 | 64 | _userIdProp: computed('user', 'config.userProperties.userIdProp', function() { 65 | return get(this, `user.${get(this, 'config.userProperties.userIdProp')}`); 66 | }), 67 | 68 | _userNameProp: computed('user', 'config.userProperties.nameProp', function() { 69 | return get(this, `user.${get(this, 'config.userProperties.nameProp')}`); 70 | }), 71 | 72 | _userEmailProp: computed('user', 'config.userProperties.emailProp', function() { 73 | return get(this, `user.${get(this, 'config.userProperties.emailProp')}`); 74 | }), 75 | 76 | _userCreatedAtProp: computed('user', 'config.userProperties.createdAtProp', function() { 77 | return get(this, `user.${get(this, 'config.userProperties.createdAtProp')}`); 78 | }), 79 | 80 | /** 81 | * Indicates the open state of the Intercom panel 82 | * 83 | * @public 84 | * @type {Boolean} 85 | */ 86 | isOpen: false, 87 | 88 | /** 89 | * Indicates whether the Intercom boot command has been called. 90 | * 91 | * @public 92 | * @readonly 93 | * @type {Boolean} 94 | */ 95 | isBooted: false, 96 | _hasUserContext: computed('user', '_userEmailProp', '_userIdProp', function() { 97 | return ( 98 | !!get(this, 'user') && 99 | (!!get(this, '_userEmailProp') || !!get(this, '_userIdProp')) 100 | ); 101 | }), 102 | /** 103 | * Reports the number of unread messages 104 | * 105 | * @public 106 | * @type {Number} 107 | */ 108 | unreadCount: 0, 109 | 110 | /** 111 | * If true, will automatically update intercom when changes to user object are made. 112 | * 113 | * @type {Boolean} 114 | * @public 115 | */ 116 | autoUpdate: true, 117 | 118 | /** 119 | * Hide the default Intercom launcher button 120 | * 121 | * @public 122 | * @type {Boolean} 123 | */ 124 | hideDefaultLauncher: false, 125 | 126 | /** 127 | * @private 128 | * alias for appId 129 | * @type {[type]} 130 | */ 131 | appId: alias('config.appId'), 132 | 133 | start(bootConfig = {}) { 134 | let _bootConfig = assign(get(this, '_intercomBootConfig'), bootConfig); 135 | this.boot(_bootConfig); 136 | }, 137 | 138 | stop() { 139 | return this.shutdown(); 140 | }, 141 | 142 | /** 143 | * Boot intercom window 144 | * @param {Object} [config={}] [description] 145 | * @public 146 | */ 147 | boot(config = {}) { 148 | this._callIntercomMethod('boot', normalizeIntercomMetadata(config)); 149 | this._addEventHandlers(); 150 | this.set('isBooted', true); 151 | }, 152 | 153 | /** 154 | * Update intercom data 155 | * @param {Object} [config={}] [description] 156 | * @public 157 | */ 158 | update(config = {}) { 159 | if (!this.get('isBooted')) { 160 | warn('Cannot call update before boot', WarnOption); 161 | return; 162 | } 163 | 164 | let _hasUserContext = this.get('_hasUserContext'); 165 | if (_hasUserContext) { 166 | this._callIntercomMethod('update', normalizeIntercomMetadata(config)); 167 | } else { 168 | warn( 169 | 'Refusing to send update to Intercom because user context is incomplete. Missing userId or email', 170 | WarnOption 171 | ); 172 | } 173 | }, 174 | 175 | /** 176 | * shutdown Intercom window 177 | * @public 178 | */ 179 | shutdown() { 180 | this.set('isBooted', false); 181 | this._hasEventHandlers = false; 182 | this._callIntercomMethod('shutdown'); 183 | }, 184 | 185 | /** 186 | * Show intercom window 187 | * @public 188 | */ 189 | show() { 190 | return this._wrapIntercomCallInPromise('show', 'show'); 191 | }, 192 | 193 | /** 194 | * hide intercom window 195 | * @public 196 | */ 197 | hide() { 198 | return this._wrapIntercomCallInPromise('hide', 'hide'); 199 | }, 200 | 201 | toggleOpen() { 202 | this.get('isOpen') ? this.hide() : this.show(); 203 | }, 204 | 205 | /** 206 | * Opens the message window with the message list visible. 207 | * 208 | * @public 209 | * @return {Promise} 210 | */ 211 | showMessages() { 212 | return this._wrapIntercomCallInPromise('showMessages', 'show'); 213 | }, 214 | 215 | /** 216 | * Opens the message window with the new message view. 217 | * 218 | * @public 219 | * @return {Promise} 220 | */ 221 | showNewMessage(initialText) { 222 | return this._wrapIntercomCallInPromise('showNewMessage', 'show', initialText); 223 | }, 224 | 225 | /** 226 | * You can submit an event using the trackEvent method. 227 | * This will associate the event with the currently logged in user and 228 | * send it to Intercom. 229 | * 230 | * The final parameter is a map that can be used to send optional 231 | * metadata about the event. 232 | * 233 | * @param {String} eventName 234 | * @param {Object} metadata 235 | * @public 236 | */ 237 | trackEvent() { 238 | this._callIntercomMethod('trackEvent', ...arguments); 239 | }, 240 | 241 | /** 242 | * A visitor is someone who goes to your site but does not use the messenger. 243 | * You can track these visitors via the visitor user_id. This user_id 244 | * can be used to retrieve the visitor or lead through the REST API. 245 | * 246 | * @public 247 | * @return {String} The visitor ID 248 | */ 249 | getVisitorId() { 250 | return this.get('api')('getVisitorId'); 251 | }, 252 | 253 | /** 254 | * If you would like to trigger a tour based on an action a user or visitor 255 | * takes in your site or application, you can use this API method. 256 | * You need to call this method with the id of the tour you wish to show. 257 | * The id of the tour can be found in the “Use tour everywhere” section 258 | * of the tour editor. 259 | * @public 260 | * @param {number} tourId Tour ID to trigger 261 | */ 262 | startTour(tourId) { 263 | return this.get('api')('startTour', tourId); 264 | }, 265 | 266 | /** 267 | * Private on hide 268 | * @private 269 | * @return {[type]} [description] 270 | */ 271 | _onHide() { 272 | this.set('isOpen', false); 273 | this.trigger('hide'); 274 | }, 275 | 276 | /** 277 | * handle onShow events 278 | * @private 279 | */ 280 | _onShow() { 281 | this.set('isOpen', true); 282 | this.trigger('show'); 283 | }, 284 | 285 | /** 286 | * Handle onUnreadCountChange Events 287 | * @param {[type]} count [description] 288 | * @private 289 | */ 290 | _onUnreadCountChange(count) { 291 | this.set('unreadCount', Number(count)); 292 | }, 293 | 294 | _addEventHandlers() { 295 | if (this._hasEventHandlers) { 296 | return; 297 | } 298 | this._callIntercomMethod('onHide', () => next(this, '_onHide')); 299 | this._callIntercomMethod('onShow', () => next(this, '_onShow')); 300 | this._callIntercomMethod('onUnreadCountChange', count => { 301 | this._onUnreadCountChange(count); 302 | }); 303 | this._hasEventHandlers = true; 304 | }, 305 | 306 | _wrapIntercomCallInPromise(intercomMethod, eventName, ...args) { 307 | return new Promise(resolve => { 308 | let isOpen = this.get('isOpen'); 309 | if ((eventName === 'show' && isOpen) || (eventName === 'hide' && !isOpen)) { 310 | next(this, resolve); 311 | } else { 312 | this.one(eventName, resolve); 313 | } 314 | this._callIntercomMethod(intercomMethod, ...args); 315 | }); 316 | }, 317 | 318 | _callIntercomMethod(methodName, ...args) { 319 | let intercom = this.get('api'); 320 | intercom(methodName, ...args); 321 | }, 322 | 323 | // eslint-disable-next-line ember/no-observers 324 | userDataDidChange: observer('user.@each', function() { 325 | if (this.get('autoUpdate') && this.get('isBooted')) { 326 | let user = this.get('_computedUser'); 327 | let appId = this.get('appId'); 328 | let config = assign({ app_id: appId}, user ); 329 | this.update(config); 330 | } 331 | }), 332 | 333 | /** 334 | * Alias for computed user data with app-provided config values 335 | * @private 336 | * @type {[type]} 337 | */ 338 | _computedUser: computed( 339 | 'user.@each', 340 | 'user', 341 | '_userHashProp', 342 | '_userIdProp', 343 | '_userNameProp', 344 | '_userEmailProp', 345 | '_userCreatedAtProp', 346 | function() { 347 | assert('You must supply an "ENV.intercom.appId" in your "config/environment.js" file.', this.get('appId')); 348 | 349 | let obj = {}; 350 | if (this.get('user')) { 351 | let userProps = Object.values(get(this, 'config.userProperties')), 352 | user = get(this, 'user'), 353 | userKeys = Object.keys(user); 354 | 355 | userKeys.forEach(k => { 356 | if (!userProps.includes(k) && !obj.hasOwnProperty(k)) { 357 | obj[k] = user[k]; 358 | } 359 | }); 360 | 361 | obj.user_hash = get(this, '_userHashProp'); 362 | obj.user_id = get(this, '_userIdProp'); 363 | obj.name = get(this, '_userNameProp'); 364 | obj.email = get(this, '_userEmailProp'); 365 | if (get(this, '_userCreatedAtProp')) { 366 | // eslint-disable-next-line 367 | obj.created_at = get(this, '_userCreatedAtProp'); 368 | } 369 | } 370 | return obj; 371 | } 372 | ), 373 | 374 | _intercomBootConfig: computed('config', 'user.@each', '_hasUserContext', 'hideDefaultLauncher', function() { 375 | let appId = get(this, 'config.appId'); 376 | let user = get(this, '_computedUser'); 377 | let _hasUserContext = get(this, '_hasUserContext'); 378 | let hideDefaultLauncher = get(this, 'hideDefaultLauncher'); 379 | 380 | assert('You must supply an "ENV.intercom.appId" in your "config/environment.js" file.', appId); 381 | 382 | let obj = { app_id: appId }; 383 | if (hideDefaultLauncher) { 384 | obj.hideDefaultLauncher = true; 385 | } 386 | 387 | if (_hasUserContext) { 388 | obj = assign({}, obj, user); 389 | } 390 | 391 | return normalizeIntercomMetadata(obj); 392 | }) 393 | }); 394 | -------------------------------------------------------------------------------- /addon/templates/components/intercom-io.hbs: -------------------------------------------------------------------------------- 1 | {{yield}} 2 | -------------------------------------------------------------------------------- /app/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/app/.gitkeep -------------------------------------------------------------------------------- /app/components/intercom-io.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-intercom-io/components/intercom-io'; 2 | -------------------------------------------------------------------------------- /app/initializers/ember-intercom.js: -------------------------------------------------------------------------------- 1 | export { default, initialize } from 'ember-intercom-io/initializers/ember-intercom'; 2 | -------------------------------------------------------------------------------- /app/services/intercom.js: -------------------------------------------------------------------------------- 1 | import { get } from '@ember/object'; 2 | import IntercomService from 'ember-intercom-io/services/intercom'; 3 | import cfg from '../config/environment'; 4 | 5 | export default IntercomService.extend({ 6 | config: get(cfg, 'intercom') ?? {} 7 | }); 8 | -------------------------------------------------------------------------------- /blueprints/ember-intercom-io/files/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: '<%= dasherizedPackageName %>', 6 | environment: environment, 7 | baseURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | contentSecurityPolicy: { 16 | 'connect-src': [ 17 | 'https://api-iam.intercom.io', 18 | 'https://api-ping.intercom.io', 19 | 'https://nexus-websocket-a.intercom.io', 20 | 'https://nexus-websocket-b.intercom.io', 21 | 'wss://nexus-websocket-a.intercom.io', 22 | 'wss://nexus-websocket-b.intercom.io'].join(' '), 23 | 'img-src': [ 24 | 'data:', 25 | 'http://localhost:4200', 26 | 'https://static.intercomcdn.com', 27 | 'https://js.intercomcdn.com'].join(' '), 28 | 'default-src': 'http://localhost:4200', 29 | 'script-src': [ 30 | 'http://localhost:4200', 31 | 'https://widget.intercom.io', 32 | 'https://js.intercomcdn.com'].join(' '), 33 | 'media-src': [ 34 | 'https://js.intercomcdn.com'].join(' '), 35 | 'style-src': [ 36 | 'http://localhost:4200', 37 | '\'unsafe-inline\''].join(' ') 38 | }, 39 | intercom: { 40 | appId: null // <-- REPLACE WITH YOUR INTERCOM.IO app_id 41 | }, 42 | APP: { 43 | // Here you can pass flags/options to your application instance 44 | // when it is created 45 | } 46 | }; 47 | 48 | if (environment === 'development') { 49 | // ENV.APP.LOG_RESOLVER = true; 50 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 51 | // ENV.APP.LOG_TRANSITIONS = true; 52 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 53 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 54 | } 55 | 56 | if (environment === 'test') { 57 | // Testem prefers this... 58 | ENV.baseURL = '/'; 59 | ENV.locationType = 'none'; 60 | 61 | // keep test console output quieter 62 | ENV.APP.LOG_ACTIVE_GENERATION = false; 63 | ENV.APP.LOG_VIEW_LOOKUPS = false; 64 | 65 | ENV.APP.rootElement = '#ember-testing'; 66 | } 67 | 68 | if (environment === 'production') { 69 | 70 | } 71 | 72 | return ENV; 73 | }; 74 | -------------------------------------------------------------------------------- /blueprints/ember-intercom-io/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | description: 'ember-intercom-io installation blueprint', 4 | 5 | normalizeEntityName() {} 6 | 7 | }; 8 | -------------------------------------------------------------------------------- /client/intercom-shim.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /* globals define */ 3 | 'use strict'; 4 | 5 | function l(config) { 6 | if (config.intercom.enabled === false) { 7 | return; 8 | } 9 | 10 | var i = function() { 11 | i.c(arguments); 12 | }; 13 | i.q = []; 14 | i.c = function(args) { 15 | i.q.push(args); 16 | }; 17 | 18 | window.Intercom = i; 19 | 20 | var d = document; 21 | var s = d.createElement('script'); 22 | s.type = 'text/javascript'; 23 | s.async = true; 24 | s.src = 'https://widget.intercom.io/widget/' + config.intercom.appId; 25 | var x = d.getElementsByTagName('script')[0]; 26 | x.parentNode.insertBefore(s, x); 27 | } 28 | 29 | var ic = window.Intercom; 30 | if (typeof ic === 'function') { 31 | ic('reattach_activator'); 32 | ic('update', {}); 33 | } 34 | 35 | function generateModule(name, values) { 36 | define(name, [], function() { 37 | 'use strict'; // jshint ignore:line 38 | 39 | return values; 40 | }); 41 | } 42 | 43 | generateModule('intercom', { 44 | default: function() { 45 | if (window.Intercom && typeof window.Intercom.apply === 'function') { 46 | return window.Intercom.apply(null, arguments); 47 | } 48 | }, 49 | _setup: l 50 | }); 51 | })(); 52 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = function() { 6 | return Promise.all([ 7 | getChannelURL('release'), 8 | getChannelURL('beta'), 9 | getChannelURL('canary') 10 | ]).then((urls) => { 11 | return { 12 | useYarn: true, 13 | scenarios: [ 14 | { 15 | name: 'ember-lts-2.16', 16 | env: { 17 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }), 18 | }, 19 | npm: { 20 | devDependencies: { 21 | '@ember/jquery': '^0.5.1', 22 | 'ember-source': '~2.16.0' 23 | } 24 | } 25 | }, 26 | { 27 | name: 'ember-lts-2.18', 28 | env: { 29 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }), 30 | }, 31 | npm: { 32 | devDependencies: { 33 | '@ember/jquery': '^0.5.1', 34 | 'ember-source': '~2.18.0' 35 | } 36 | } 37 | }, 38 | { 39 | name: 'ember-release', 40 | npm: { 41 | devDependencies: { 42 | 'ember-source': urls[0] 43 | } 44 | } 45 | }, 46 | { 47 | name: 'ember-beta', 48 | npm: { 49 | devDependencies: { 50 | 'ember-source': urls[1] 51 | } 52 | } 53 | }, 54 | { 55 | name: 'ember-canary', 56 | npm: { 57 | devDependencies: { 58 | 'ember-source': urls[2] 59 | } 60 | } 61 | }, 62 | { 63 | name: 'ember-default', 64 | npm: { 65 | devDependencies: {} 66 | } 67 | }, 68 | { 69 | name: 'ember-default-with-jquery', 70 | env: { 71 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 72 | 'jquery-integration': true 73 | }) 74 | }, 75 | npm: { 76 | devDependencies: { 77 | '@ember/jquery': '^0.5.1' 78 | } 79 | } 80 | } 81 | ] 82 | }; 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { 5 | intercom: { 6 | appId: process.env.INTERCOM_APP_ID | null, 7 | userProperties: { 8 | createdAtProp: 'createdAt', 9 | emailProp: 'email', 10 | nameProp: 'name', 11 | userHashProp: 'hash', 12 | userIdProp: 'id' 13 | } 14 | } 15 | }; 16 | }; 17 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | let app = new EmberAddon(defaults, { 7 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | return app.toTree(); 18 | }; 19 | -------------------------------------------------------------------------------- /ember-intercom.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "folder_exclude_patterns": ["dist", "bower_components", "node_modules"], 6 | "path": "." 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 'use strict'; 3 | 4 | var path = require('path'); 5 | 6 | module.exports = { 7 | name: require('./package').name, 8 | options: { 9 | babel: { 10 | plugins: ['@babel/proposal-object-rest-spread'] 11 | } 12 | }, 13 | included: function(app) { 14 | this._super.included(app); 15 | app.import('vendor/intercom-shim.js'); 16 | }, 17 | 18 | treeForVendor: function() { 19 | return path.join(__dirname, 'client'); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | {"compilerOptions":{"target":"es6","experimentalDecorators":true},"exclude":["node_modules","bower_components","tmp","vendor",".git","dist"]} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-intercom-io", 3 | "version": "0.0.0-development", 4 | "description": "Idiomatic, deep Intercom.io integration for ember.js apps", 5 | "scripts": { 6 | "build": "ember build", 7 | "lint:hbs": "ember-template-lint .", 8 | "lint:js": "eslint .", 9 | "start": "ember server", 10 | "test": "ember test", 11 | "test:all": "ember try:each", 12 | "semantic-release": "semantic-release" 13 | }, 14 | "engines": { 15 | "node": "6.* || 8.* || >= 10.*" 16 | }, 17 | "devDependencies": { 18 | "@commitlint/cli": "7.6.0", 19 | "@commitlint/config-conventional": "7.6.0", 20 | "@commitlint/travis-cli": "7.6.0", 21 | "@ember/optional-features": "1.0.0", 22 | "@mike-north/js-lib-renovate-config": "1.1.5", 23 | "@mike-north/js-lib-semantic-release-config": "1.0.1", 24 | "@types/ember": "3.1.2", 25 | "@types/ember-qunit": "3.4.10", 26 | "@types/ember__test-helpers": "0.7.12", 27 | "broccoli-asset-rev": "3.0.0", 28 | "ember-cli": "3.28.6", 29 | "ember-cli-dependency-checker": "3.3.1", 30 | "ember-cli-eslint": "5.1.0", 31 | "ember-cli-fastboot": "2.2.3", 32 | "ember-cli-htmlbars-inline-precompile": "3.0.2", 33 | "ember-cli-inject-live-reload": "2.1.0", 34 | "ember-cli-release": "1.0.0-beta.2", 35 | "ember-cli-template-lint": "1.0.0-beta.3", 36 | "ember-cli-uglify": "3.0.0", 37 | "ember-disable-prototype-extensions": "1.1.3", 38 | "ember-export-application-global": "2.0.1", 39 | "ember-load-initializers": "2.1.2", 40 | "ember-maybe-import-regenerator": "0.1.6", 41 | "ember-qunit": "5.1.5", 42 | "ember-resolver": "8.1.0", 43 | "ember-sinon": "4.1.1", 44 | "ember-sinon-qunit": "4.0.1", 45 | "ember-source": "4.11.0", 46 | "ember-source-channel-url": "1.2.0", 47 | "ember-try": "1.2.1", 48 | "eslint-plugin-ember": "10.6.1", 49 | "eslint-plugin-node": "11.1.0", 50 | "husky": "3.0.7", 51 | "loader.js": "4.7.0", 52 | "qunit-dom": "2.0.0", 53 | "semantic-release": "15.12.5" 54 | }, 55 | "keywords": [ 56 | "ember-addon", 57 | "intercom", 58 | "chat" 59 | ], 60 | "repository": "https://github.com/mike-north/ember-intercom-io", 61 | "license": "MIT", 62 | "author": "Mike North (http://mike.works)", 63 | "directories": { 64 | "doc": "doc", 65 | "test": "tests" 66 | }, 67 | "dependencies": { 68 | "@babel/plugin-proposal-object-rest-spread": "^7.2.0", 69 | "ember-cli-babel": "^7.1.3", 70 | "ember-cli-htmlbars": "^6.0.0" 71 | }, 72 | "ember-addon": { 73 | "configPath": "tests/dummy/config" 74 | }, 75 | "commitlint": { 76 | "extends": [ 77 | "@commitlint/config-conventional" 78 | ] 79 | }, 80 | "husky": { 81 | "hooks": { 82 | "commit-msg": "./node_modules/.bin/commitlint -e $HUSKY_GIT_PARAMS" 83 | } 84 | }, 85 | "release": { 86 | "extends": "@mike-north/js-lib-semantic-release-config" 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@mike-north/js-lib-renovate-config"] 3 | } 4 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html?hidepassed', 3 | disable_watching: true, 4 | launch_in_ci: [ 5 | 'Chrome' 6 | ], 7 | launch_in_dev: [ 8 | 'Chrome' 9 | ], 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-gpu', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900' 22 | ].filter(Boolean) 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | module.exports = { 3 | env: { 4 | embertest: true 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | const App = Application.extend({ 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | Resolver 10 | }); 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | const Router = EmberRouter.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | this.route('help'); 11 | }); 12 | 13 | export default Router; 14 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | font-family: 'Helvetica Neue' Helvetica Sans-Serif; 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Ember-Intercom

2 | 3 | 4 | {{outlet}} 5 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/templates/help.hbs: -------------------------------------------------------------------------------- 1 | 2 |

The widget should now be active! Click the back button (or {{link-to "Click here" "index"}}) to go to a page where the intercom widget is inactive

3 | 4 | {{intercom-io}} 5 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 |

Intercom.io is a slick in-app chat widget (and much more)

2 | 3 |

{{#link-to "help"}}Click here{{/link-to}} to go to a page where the intercom chat widget is active

-------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | /* jshint node: true */ 2 | 3 | module.exports = function(environment) { 4 | var ENV = { 5 | modulePrefix: 'dummy', 6 | environment: environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. 'with-controller': true 13 | } 14 | }, 15 | contentSecurityPolicy: { 16 | 'connect-src': [ 17 | 'https://api-iam.intercom.io', 18 | 'https://api-ping.intercom.io', 19 | 'https://nexus-websocket-a.intercom.io', 20 | 'https://nexus-websocket-b.intercom.io', 21 | 'wss://nexus-websocket-a.intercom.io', 22 | 'wss://nexus-websocket-b.intercom.io' 23 | ].join(' '), 24 | 'img-src': [ 25 | 'data:', 26 | 'http://localhost:4200', 27 | 'https://static.intercomcdn.com', 28 | 'https://js.intercomcdn.com' 29 | ].join(' '), 30 | 'default-src': 'http://localhost:4200', 31 | 'script-src': ['http://localhost:4200', 'https://widget.intercom.io', 'https://js.intercomcdn.com'].join(' '), 32 | 'media-src': ['https://js.intercomcdn.com'].join(' '), 33 | 'style-src': ['http://localhost:4200', "'unsafe-inline'"].join(' ') 34 | }, 35 | intercom: { 36 | appId: process.env.INTERCOM_APP_ID || 'null' 37 | }, 38 | APP: { 39 | // Here you can pass flags/options to your application instance 40 | // when it is created 41 | } 42 | }; 43 | 44 | if (environment === 'development') { 45 | // ENV.APP.LOG_RESOLVER = true; 46 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 47 | // ENV.APP.LOG_TRANSITIONS = true; 48 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 49 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 50 | } 51 | 52 | if (environment === 'test') { 53 | // Testem prefers this... 54 | ENV.locationType = 'none'; 55 | 56 | ENV.EmberENV.RAISE_ON_DEPRECATION = !process.env['ALLOW_DEPRECATIONS']; 57 | 58 | // keep test console output quieter 59 | ENV.APP.LOG_ACTIVE_GENERATION = false; 60 | ENV.APP.LOG_VIEW_LOOKUPS = false; 61 | 62 | ENV.APP.rootElement = '#ember-testing'; 63 | ENV.APP.autoboot = false; 64 | } 65 | 66 | if (environment === 'production') { 67 | // prod flags 68 | } 69 | 70 | return ENV; 71 | }; 72 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "jquery-integration": false 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions' 7 | ]; 8 | 9 | const isCI = !!process.env.CI; 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/public/crossdomain.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | 16 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/helpers/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from '../../resolver'; 2 | import config from '../../config/environment'; 3 | 4 | const resolver = Resolver.create(); 5 | 6 | resolver.namespace = { 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix 9 | }; 10 | 11 | export default resolver; 12 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/components/intercom-io-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | let intercomCommandArgs = {}; 7 | 8 | let intercomStub = function(command, arg) { 9 | if (!intercomCommandArgs[command]) { 10 | intercomCommandArgs[command] = []; 11 | } 12 | intercomCommandArgs[command].push(arg || null); 13 | }; 14 | 15 | const mockConfig = { 16 | intercom: { 17 | userProperties: { 18 | nameProp: 'name', 19 | emailProp: 'email', 20 | userHashProp: 'hash', 21 | userIdProp: 'id', 22 | createdAtProp: 'createdAt' 23 | }, 24 | appId: '1' 25 | } 26 | }; 27 | 28 | module('Integration | Component | intercom-io', function(hooks) { 29 | setupRenderingTest(hooks); 30 | 31 | test('it renders', async function(assert) { 32 | this.owner.register('service:config', mockConfig, { instantiate: false }); 33 | this.set('intercom', this.owner.lookup('service:intercom')); 34 | this.set('intercom.api', intercomStub); 35 | assert.expect(2); 36 | let oldStartCount = (intercomCommandArgs.boot || []).length; 37 | 38 | await render(hbs`{{intercom-io}}`); 39 | 40 | assert.equal(this.element.textContent.trim(), ''); 41 | assert.equal(intercomCommandArgs.boot.length - oldStartCount, 1, 'Intercom service "start" was invoked'); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/initializers/ember-intercom-test.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import { run } from '@ember/runloop'; 3 | import EmberIntercomInitializer from 'dummy/initializers/ember-intercom'; 4 | import { module, test } from 'qunit'; 5 | 6 | let application; 7 | 8 | // jscs:disable requireCamelCaseOrUpperCaseIdentifiers 9 | const mockConfig = { 10 | intercom: { 11 | app_id: '1' // eslint-disable-line 12 | } 13 | }; 14 | // jscs:enable requireCamelCaseOrUpperCaseIdentifiers 15 | 16 | module('Unit | Initializer | ember intercom', { 17 | beforeEach() { 18 | run(() => { 19 | application = Application.create(); 20 | application.register('config:environment', mockConfig, { instantiate: false }); 21 | application.deferReadiness(); 22 | }); 23 | } 24 | }); 25 | 26 | // Replace this with your real tests. 27 | test('it works', function(assert) { 28 | EmberIntercomInitializer.initialize(application); 29 | 30 | // you would normally confirm the results of the initializer here 31 | assert.ok(true); 32 | }); 33 | -------------------------------------------------------------------------------- /tests/unit/services/intercom-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupTest } from 'ember-qunit'; 3 | import sinon from 'sinon'; 4 | import { setProperties } from '@ember/object'; 5 | import { settled } from '@ember/test-helpers'; 6 | 7 | const mockConfig = { 8 | intercom: { 9 | userProperties: { 10 | nameProp: 'name', 11 | emailProp: 'email', 12 | userHashProp: 'hash', 13 | userIdProp: 'id', 14 | createdAtProp: 'createdAt' 15 | }, 16 | appId: '1' 17 | } 18 | }; 19 | let intercomStub; 20 | 21 | module('Unit | Service | intercom', function(hooks) { 22 | 23 | setupTest(hooks); 24 | hooks.beforeEach(function() { 25 | this.owner.register('service:config', mockConfig, { instantiate: false }); 26 | 27 | let service = this.owner.lookup('service:intercom'); 28 | 29 | intercomStub = sinon.stub(); 30 | 31 | service.set('api', intercomStub); 32 | service.set('config', mockConfig.intercom); 33 | }); 34 | 35 | 36 | // Replace this with your real tests. 37 | test('it exists', function(assert) { 38 | let service = this.owner.lookup('service:intercom'); 39 | assert.ok(service); 40 | }); 41 | 42 | test('it adds the correct user context to the boot config', async function(assert) { 43 | let now = new Date(); 44 | let actualUser = { 45 | id: 'fooUserId', 46 | name: 'foo', 47 | email: 'foo@foo.com', 48 | hash: 'fakeIntercomSecureUserHash', 49 | createdAt: now, 50 | custom: 'my-custom-property', 51 | shouldBeUnderScored: 'myThing', 52 | company: { 53 | id: 'companyId', 54 | nestedUnderscore: 'custom-nested' 55 | } 56 | }; 57 | let service = this.owner.lookup('service:intercom'); 58 | 59 | setProperties(service.user, actualUser); 60 | 61 | service.start(); 62 | 63 | // jscs:disable requireCamelCaseOrUpperCaseIdentifiers 64 | let expectedBootConfig = { 65 | app_id: mockConfig.intercom.appId, //eslint-disable-line 66 | name: actualUser.name, 67 | email: actualUser.email, 68 | user_hash: actualUser.hash, 69 | user_id: actualUser.id, 70 | created_at: actualUser.createdAt.valueOf(), //eslint-disable-line 71 | custom: actualUser.custom, 72 | should_be_under_scored: actualUser.shouldBeUnderScored, 73 | company: { 74 | id: 'companyId', 75 | nested_underscore: actualUser.company.nestedUnderscore 76 | } 77 | }; 78 | await settled(); 79 | // jscs:enable requireCamelCaseOrUpperCaseIdentifiers 80 | assert.deepEqual( 81 | intercomStub.firstCall.args, 82 | ['boot', expectedBootConfig], 83 | 'it called the intercom module with boot' 84 | ); 85 | sinon.assert.calledWith(intercomStub, 'boot', expectedBootConfig); 86 | await settled(); 87 | assert.equal(intercomStub.calledWith('onHide'), true, 'it called the intercom module with onHide'); 88 | assert.equal(intercomStub.calledWith('onShow'), true, 'it called the intercom module with onShow'); 89 | assert.equal(intercomStub.calledWith('onUnreadCountChange'), true, 'it called the intercom module with onUnreadCountChange'); 90 | }); 91 | 92 | test('update gets called when user properties change', function(assert) { 93 | let service = this.owner.lookup('service:intercom'); 94 | 95 | let expectedConfig = { 96 | name: 'Bobby Tables', 97 | email: 'user@example.com', 98 | app_id: mockConfig.intercom.appId //eslint-disable-line 99 | }; 100 | 101 | service.boot(); 102 | 103 | service.setProperties({ 104 | user: { 105 | name: 'Bobby Tables', 106 | email: 'user@example.com' 107 | } 108 | }); 109 | 110 | assert.deepEqual( 111 | intercomStub.lastCall.args, 112 | ['update', expectedConfig], 113 | 'it called the intercom module with update' 114 | ); 115 | }); 116 | 117 | test('Track events in intercom', function(assert) { 118 | let service = this.owner.lookup('service:intercom'); 119 | /* eslint-disable camelcase */ 120 | let eventName = 'invited-friend'; 121 | let metadata = { 122 | friend_name: 'bobby tables', 123 | friend_email: 'bobby@tables.com' 124 | }; 125 | /* eslint-enable camelcase */ 126 | 127 | service.boot(); 128 | service.trackEvent(eventName, metadata); 129 | 130 | assert.equal(intercomStub.calledWith('trackEvent'), true, 'Intercom track event method called'); 131 | sinon.assert.calledWith(intercomStub, 'trackEvent', eventName, metadata); 132 | }); 133 | 134 | test('Calls out to methods in intercom API', function(assert) { 135 | let service = this.owner.lookup('service:intercom'); 136 | let methods = ['getVisitorId', 'show', 'hide', 'showMessages']; 137 | 138 | service.boot(); 139 | methods.forEach(method => { 140 | service[method](); 141 | assert.equal(intercomStub.calledWith(method), true, `Intercom method called -- ${method}`); 142 | }); 143 | 144 | service.startTour(123); 145 | assert.equal(intercomStub.calledWith('startTour'), true, 'Intercom method called -- startTour'); 146 | sinon.assert.calledWith(intercomStub, 'startTour', 123); 147 | }); 148 | }); 149 | -------------------------------------------------------------------------------- /vendor/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mike-north/ember-intercom-io/bb73ad16423c8216d7f928d65ca4fe56b8a6f0ac/vendor/.gitkeep --------------------------------------------------------------------------------