1+ declare global {
2+ interface Window {
3+ Pylon ?: ( ( ...args : unknown [ ] ) => void ) & { q ?: unknown [ ] [ ] }
4+ pylon ?: { chat_settings : Record < string , string | undefined > }
5+ }
6+ }
7+
18import flagsmith from '@flagsmith/flagsmith'
2- import moment from 'moment'
39import AccountStore from './stores/account-store'
4- import { getStore } from './store'
5- import { selectBuildVersion } from './services/useBuildVersion'
610import getUserDisplayName from './utils/getUserDisplayName'
7- import { AccountModel , Organisation } from './types/responses'
11+ import Utils , { planNames } from './utils/utils'
12+ import { AccountModel } from './types/responses'
813import Project from './project'
914
10- // Used in self-hosted pages where users can opt into reaching out
11- const defaultCrispID = '8857f89e-0eb5-4263-ab49-a293872b6c19'
1215const defaultPylonID = '028babb7-d93f-4e32-be6a-59db190a084f'
1316
14- async function loadCrisp ( crispWebsiteId : string ) {
15- // @ts -ignore
16- if ( window . $crisp ) {
17- return
18- }
19- // @ts -ignore
20- window . $crisp = [ ]
21- // @ts -ignore
22- window . CRISP_WEBSITE_ID = crispWebsiteId
23- await new Promise ( ( resolve , reject ) => {
24- const d = document
25- const s = d . createElement ( 'script' )
26- s . src = 'https://client.crisp.chat/l.js'
27- s . async = true
28- s . onload = resolve
29- s . onerror = reject
30- d . getElementsByTagName ( 'head' ) [ 0 ] . appendChild ( s )
31- } )
17+ function isFreePlan ( ) : boolean {
18+ const plan = AccountStore . getActiveOrgPlan ( )
19+ return Utils . getPlanName ( plan ) === planNames . free
3220}
3321
3422async function loadPylon ( pylonAppId : string ) {
35- // @ts -ignore
3623 if ( window . Pylon ) {
3724 return
3825 }
3926
40- // Initialize Pylon using their recommended script format
4127 await new Promise ( ( resolve , reject ) => {
4228 const t = document
43- const n = function ( ...args : any [ ] ) {
44- // @ts -ignore
45- n . q . push ( args )
46- }
47- // @ts -ignore
48- n . q = [ ]
49- // @ts -ignore
29+ const n : ( ( ...args : unknown [ ] ) => void ) & { q ?: unknown [ ] [ ] } =
30+ Object . assign ( ( ...args : unknown [ ] ) => n . q ! . push ( args ) , { q : [ ] } )
5031 window . Pylon = n
5132
5233 const s = t . createElement ( 'script' )
@@ -60,57 +41,8 @@ async function loadPylon(pylonAppId: string) {
6041 } )
6142}
6243
63- function setupCrisp ( ) {
64- const user = AccountStore . model as AccountModel
65- if ( typeof $crisp === 'undefined' || ! user ) {
66- return
67- }
68- const isSaas = ( ) =>
69- selectBuildVersion ( getStore ( ) . getState ( ) ) ?. backend ?. is_saas
70- $crisp . push ( [
71- 'set' ,
72- 'session:data' ,
73- [ [ [ 'hosting' , isSaas ( ) ? 'SaaS' : 'Self-Hosted' ] ] ] ,
74- ] )
75- const organisation = AccountStore . getOrganisation ( ) as Organisation
76- const formatOrganisation = ( o : Organisation ) => {
77- const plan = AccountStore . getActiveOrgPlan ( )
78- return `${ o . name } (${ plan } ) #${ o . id } `
79- }
80- const otherOrgs = user ?. organisations . filter ( ( v ) => v . id !== organisation ?. id )
81- if ( window . $crisp ) {
82- $crisp . push ( [ 'set' , 'user:email' , user . email ] )
83- $crisp . push ( [ 'set' , 'user:nickname' , `${ getUserDisplayName ( user ) } ` ] )
84- if ( otherOrgs . length ) {
85- $crisp . push ( [
86- 'set' ,
87- 'session:data' ,
88- [ [ [ 'other-orgs' , `${ otherOrgs ?. length } other organisations` ] ] ] ,
89- ] )
90- }
91- $crisp . push ( [
92- 'set' ,
93- 'session:data' ,
94- [
95- [
96- [ 'user-id' , `${ user . id } ` ] ,
97- [ 'date-joined' , `${ moment ( user . date_joined ) . format ( 'Do MMM YYYY' ) } ` ] ,
98- ] ,
99- ] ,
100- ] )
101- if ( organisation ) {
102- $crisp . push ( [ 'set' , 'user:company' , formatOrganisation ( organisation ) ] )
103- $crisp . push ( [
104- 'set' ,
105- 'session:data' ,
106- [ [ [ 'seats' , organisation . num_seats ] ] ] ,
107- ] )
108- }
109- }
110- }
111-
11244function setupPylon ( ) {
113- const user = AccountStore . model as AccountModel
45+ const user = AccountStore . getUser ( ) as AccountModel
11446 if ( typeof window . Pylon === 'undefined' || ! user ) {
11547 return
11648 }
@@ -131,24 +63,19 @@ function setupPylon() {
13163}
13264
13365export function identifyChatUser ( ) {
134- const usePylon = flagsmith . hasFeature ( 'pylon_chat' )
135-
136- if ( usePylon ) {
66+ if ( flagsmith . hasFeature ( 'pylon_chat' ) && ! isFreePlan ( ) ) {
13767 setupPylon ( )
138- } else {
139- setupCrisp ( )
14068 }
14169}
14270
14371export function openChat ( ) {
144- const usePylon = flagsmith . hasFeature ( 'pylon_chat' )
145-
146- if ( usePylon && typeof window . Pylon !== 'undefined' ) {
72+ if (
73+ flagsmith . hasFeature ( 'pylon_chat' ) &&
74+ ! isFreePlan ( ) &&
75+ typeof window . Pylon !== 'undefined'
76+ ) {
14777 window . Pylon ( 'show' )
14878 setupPylon ( )
149- } else if ( typeof $crisp !== 'undefined' ) {
150- $crisp . push ( [ 'do' , 'chat:open' ] )
151- setupCrisp ( )
15279 }
15380}
15481
@@ -157,14 +84,11 @@ export default async function loadChat(forceDefaultAPIKey?: boolean) {
15784 const isWidget = document . location . href . includes ( '/widget' )
15885 if ( isWidget ) return
15986
160- const usePylon = flagsmith . hasFeature ( 'pylon_chat' )
161- const pylonId = forceDefaultAPIKey ? defaultPylonID : Project . pylonAppId
162- const crispId = forceDefaultAPIKey ? defaultCrispID : Project . crispChat
163-
164- if ( usePylon && pylonId ) {
165- await loadPylon ( pylonId )
166- } else if ( crispId ) {
167- await loadCrisp ( crispId )
87+ if ( flagsmith . hasFeature ( 'pylon_chat' ) && ! isFreePlan ( ) ) {
88+ const pylonId = forceDefaultAPIKey ? defaultPylonID : Project . pylonAppId
89+ if ( pylonId ) {
90+ await loadPylon ( pylonId )
91+ }
16892 }
16993 } catch ( error ) {
17094 console . error ( 'Failed to initialize chat widget:' , error )
0 commit comments