@@ -15,23 +15,46 @@ interface TawkVisitor {
1515 name ?: string
1616 email ?: string
1717 hash ?: string
18+ phone ?: string
19+ userId ?: string
20+ }
21+
22+ interface TawkCustomStyle {
23+ zIndex ?: number | string
1824}
1925
2026interface TawkAPI {
2127 visitor ?: TawkVisitor
28+ customStyle ?: TawkCustomStyle
29+ autoStart ?: boolean
2230 setAttributes ?: ( attrs : Record < string , unknown > , cb ?: ( err ?: Error ) => void ) => void
2331 addEvent ?: ( event : string , metadata ?: Record < string , unknown > , cb ?: ( err ?: Error ) => void ) => void
32+ addTags ?: ( tags : string [ ] , cb ?: ( err ?: Error ) => void ) => void
33+ removeTags ?: ( tags : string [ ] , cb ?: ( err ?: Error ) => void ) => void
2434 showWidget ?: ( ) => void
2535 hideWidget ?: ( ) => void
36+ toggleVisibility ?: ( ) => void
2637 maximize ?: ( ) => void
2738 minimize ?: ( ) => void
2839 toggle ?: ( ) => void
2940 popup ?: ( ) => void
3041 endChat ?: ( ) => void
42+ start ?: ( options ?: { showWidget ?: boolean } ) => void
43+ getStatus ?: ( ) => "online" | "away" | "offline"
44+ getWindowType ?: ( ) => "inline" | "embed"
45+ isChatMaximized ?: ( ) => boolean
46+ isChatMinimized ?: ( ) => boolean
47+ isChatHidden ?: ( ) => boolean
48+ isChatOngoing ?: ( ) => boolean
49+ isVisitorEngaged ?: ( ) => boolean
3150 login ?: ( user : TawkVisitor , cb ?: ( err ?: Error ) => void ) => void
3251 logout ?: ( cb ?: ( err ?: Error ) => void ) => void
33- switchWidget ?: ( options : { propertyId : string ; widgetId : string } ) => void
52+ switchWidget ?: (
53+ options : { propertyId : string ; widgetId : string } ,
54+ cb ?: ( err ?: Error ) => void ,
55+ ) => void
3456 // Event hooks Tawk exposes; each must be assignable.
57+ onBeforeLoad ?: ( ) => void
3558 onLoad ?: ( ) => void
3659 onStatusChange ?: ( status : "online" | "away" | "offline" ) => void
3760 onChatMaximized ?: ( ) => void
@@ -49,6 +72,8 @@ interface TawkAPI {
4972 onFileUpload ?: ( url : string ) => void
5073 onTagsUpdated ?: ( data : unknown ) => void
5174 onUnreadCountChanged ?: ( count : number ) => void
75+ onPrechatSubmit ?: ( data : unknown ) => void
76+ onOfflineSubmit ?: ( data : unknown ) => void
5277}
5378
5479interface TawkWindow {
@@ -70,9 +95,32 @@ const queue = createQueue<TawkAPI>()
7095const store = createIdentityStore ( )
7196const lifecycle = createLifecycle ( )
7297const unreadListeners = new Set < ( count : number ) => void > ( )
98+ const eventListeners = new Map < string , Set < ( payload ?: unknown ) => void > > ( )
7399let readyPromise : Promise < void > | undefined
74100let readyResolve : ( ( ) => void ) | undefined
75101
102+ const HOOK_TO_EVENT : Record < string , string > = {
103+ onBeforeLoad : "beforeLoad" ,
104+ onLoad : "load" ,
105+ onStatusChange : "statusChange" ,
106+ onChatMaximized : "chatMaximized" ,
107+ onChatMinimized : "chatMinimized" ,
108+ onChatHidden : "chatHidden" ,
109+ onChatStarted : "chatStarted" ,
110+ onChatEnded : "chatEnded" ,
111+ onChatMessageVisitor : "chatMessageVisitor" ,
112+ onChatMessageAgent : "chatMessageAgent" ,
113+ onChatMessageSystem : "chatMessageSystem" ,
114+ onAgentJoinChat : "agentJoinChat" ,
115+ onAgentLeaveChat : "agentLeaveChat" ,
116+ onChatSatisfaction : "chatSatisfaction" ,
117+ onVisitorNameChanged : "visitorNameChanged" ,
118+ onFileUpload : "fileUpload" ,
119+ onTagsUpdated : "tagsUpdated" ,
120+ onPrechatSubmit : "prechatSubmit" ,
121+ onOfflineSubmit : "offlineSubmit" ,
122+ }
123+
76124const TAWK_COLLISION_SYMBOLS = [ "L" , "R" , "T" ]
77125
78126function warnCollisions ( ) : void {
@@ -87,9 +135,38 @@ function warnCollisions(): void {
87135 }
88136}
89137
138+ export type TawkEventName =
139+ | "beforeLoad"
140+ | "load"
141+ | "statusChange"
142+ | "chatMaximized"
143+ | "chatMinimized"
144+ | "chatHidden"
145+ | "chatStarted"
146+ | "chatEnded"
147+ | "chatMessageVisitor"
148+ | "chatMessageAgent"
149+ | "chatMessageSystem"
150+ | "agentJoinChat"
151+ | "agentLeaveChat"
152+ | "chatSatisfaction"
153+ | "visitorNameChanged"
154+ | "fileUpload"
155+ | "tagsUpdated"
156+ | "prechatSubmit"
157+ | "offlineSubmit"
158+
90159export interface TawkLoadOptions extends LoadOptions {
91160 propertyId : string
92161 widgetId ?: string
162+ /** Visitor preload — must be assigned before the embed script downloads. */
163+ visitor ?: { name ?: string ; email ?: string ; hash ?: string ; phone ?: string ; userId ?: string }
164+ /** Tawk's only documented customStyle field. */
165+ customStyleZIndex ?: number | string
166+ /** When false, defers the socket connection until start() is called. Default: true. */
167+ autoStart ?: boolean
168+ /** Pre-load hook fired before the embed script downloads. */
169+ onBeforeLoad ?: ( ) => void
93170}
94171
95172export async function load ( options : TawkLoadOptions ) : Promise < void > {
@@ -108,9 +185,31 @@ export async function load(options: TawkLoadOptions): Promise<void> {
108185 await waitForDefer ( options . defer ?? "immediate" )
109186 const a = api ( )
110187 w ( ) . Tawk_LoadStart = new Date ( )
111- a . onLoad = ( ) => {
112- queue . ready ( a )
113- readyResolve ?.( )
188+ if ( options . visitor ) a . visitor = { ...options . visitor }
189+ if ( options . customStyleZIndex !== undefined ) {
190+ a . customStyle = { ...a . customStyle , zIndex : options . customStyleZIndex }
191+ }
192+ if ( options . autoStart === false ) a . autoStart = false
193+ // If the caller passed onBeforeLoad as a load option, register it through
194+ // the same emitter so the bridge below doesn't clobber it.
195+ if ( options . onBeforeLoad ) {
196+ let set = eventListeners . get ( "beforeLoad" )
197+ if ( ! set ) {
198+ set = new Set ( )
199+ eventListeners . set ( "beforeLoad" , set )
200+ }
201+ set . add ( options . onBeforeLoad )
202+ }
203+ // Wire every documented Tawk hook to our typed emitter (multi-listener bridge).
204+ for ( const [ hook , eventName ] of Object . entries ( HOOK_TO_EVENT ) ) {
205+ ; ( a as unknown as Record < string , ( payload ?: unknown ) => void > ) [ hook ] = ( payload ) => {
206+ const set = eventListeners . get ( eventName )
207+ if ( set ) for ( const l of set ) l ( payload )
208+ if ( eventName === "load" ) {
209+ queue . ready ( a )
210+ readyResolve ?.( )
211+ }
212+ }
114213 }
115214 a . onUnreadCountChanged = ( count : number ) => {
116215 for ( const l of unreadListeners ) l ( count )
@@ -142,6 +241,7 @@ export function identify(identity: Identity): Promise<void> {
142241 const attrs : Record < string , unknown > = { }
143242 if ( identity . name ) attrs [ "name" ] = identity . name
144243 if ( identity . email ) attrs [ "email" ] = identity . email
244+ if ( identity . phone ) attrs [ "phone" ] = identity . phone
145245 if ( identity . verification ?. kind === "hmac" ) attrs [ "hash" ] = identity . verification . hash
146246 if ( identity . attributes ) Object . assign ( attrs , identity . attributes )
147247 a . setAttributes ?.( attrs , ( err ) => {
@@ -205,11 +305,126 @@ export function onUnreadCountChange(listener: (count: number) => void): () => vo
205305 return ( ) => unreadListeners . delete ( listener )
206306}
207307
208- export function switchWidget ( options : { propertyId : string ; widgetId : string } ) : Promise < void > {
308+ export function switchWidget (
309+ options : { propertyId : string ; widgetId : string } ,
310+ cb ?: ( err ?: Error ) => void ,
311+ ) : Promise < void > {
312+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
313+ return queue . enqueue ( ( a ) => {
314+ a . switchWidget ?.( { propertyId : options . propertyId , widgetId : options . widgetId } , cb )
315+ } )
316+ }
317+
318+ function callMethod ( name : keyof TawkAPI , ...args : unknown [ ] ) : Promise < void > {
209319 if ( ! isBrowser ( ) ) return Promise . resolve ( )
210320 return queue . enqueue ( ( a ) => {
211- a . switchWidget ?.( { propertyId : options . propertyId , widgetId : options . widgetId } )
321+ const fn = a [ name ] as ( ( ...rest : unknown [ ] ) => unknown ) | undefined
322+ fn ?.( ...args )
323+ } )
324+ }
325+
326+ export function maximize ( ) : Promise < void > {
327+ return callMethod ( "maximize" )
328+ }
329+
330+ export function minimize ( ) : Promise < void > {
331+ return callMethod ( "minimize" )
332+ }
333+
334+ export function toggle ( ) : Promise < void > {
335+ return callMethod ( "toggle" )
336+ }
337+
338+ export function popup ( ) : Promise < void > {
339+ return callMethod ( "popup" )
340+ }
341+
342+ export function toggleVisibility ( ) : Promise < void > {
343+ return callMethod ( "toggleVisibility" )
344+ }
345+
346+ export function endChat ( ) : Promise < void > {
347+ return callMethod ( "endChat" )
348+ }
349+
350+ export function start ( options ?: { showWidget ?: boolean } ) : Promise < void > {
351+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
352+ return queue . enqueue ( ( a ) => a . start ?.( options ) )
353+ }
354+
355+ export function addTags ( tags : string [ ] , cb ?: ( err ?: Error ) => void ) : Promise < void > {
356+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
357+ return queue . enqueue ( ( a ) => a . addTags ?.( tags , cb ) )
358+ }
359+
360+ export function removeTags ( tags : string [ ] , cb ?: ( err ?: Error ) => void ) : Promise < void > {
361+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
362+ return queue . enqueue ( ( a ) => a . removeTags ?.( tags , cb ) )
363+ }
364+
365+ export function login (
366+ user : { name ?: string ; email ?: string ; phone ?: string ; hash ?: string ; userId ?: string } ,
367+ cb ?: ( err ?: Error ) => void ,
368+ ) : Promise < void > {
369+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
370+ store . identify ( {
371+ id : user . userId ,
372+ name : user . name ,
373+ email : user . email ,
374+ phone : user . phone ,
375+ ...( user . hash ? { verification : { kind : "hmac" as const , hash : user . hash } } : { } ) ,
212376 } )
377+ return queue . enqueue ( ( a ) => a . login ?.( user , cb ) )
378+ }
379+
380+ export function logout ( cb ?: ( err ?: Error ) => void ) : Promise < void > {
381+ if ( ! isBrowser ( ) ) return Promise . resolve ( )
382+ store . reset ( )
383+ return queue . enqueue ( ( a ) => a . logout ?.( cb ) )
384+ }
385+
386+ function syncRead < T > ( name : keyof TawkAPI ) : T | undefined {
387+ if ( ! isBrowser ( ) ) return undefined
388+ const fn = w ( ) . Tawk_API ?. [ name ] as ( ( ) => T ) | undefined
389+ return fn ?.( )
390+ }
391+
392+ export function getStatus ( ) : "online" | "away" | "offline" | undefined {
393+ return syncRead < "online" | "away" | "offline" > ( "getStatus" )
394+ }
395+
396+ export function getWindowType ( ) : "inline" | "embed" | undefined {
397+ return syncRead < "inline" | "embed" > ( "getWindowType" )
398+ }
399+
400+ export function isChatMaximized ( ) : boolean | undefined {
401+ return syncRead < boolean > ( "isChatMaximized" )
402+ }
403+
404+ export function isChatMinimized ( ) : boolean | undefined {
405+ return syncRead < boolean > ( "isChatMinimized" )
406+ }
407+
408+ export function isChatHidden ( ) : boolean | undefined {
409+ return syncRead < boolean > ( "isChatHidden" )
410+ }
411+
412+ export function isChatOngoing ( ) : boolean | undefined {
413+ return syncRead < boolean > ( "isChatOngoing" )
414+ }
415+
416+ export function isVisitorEngaged ( ) : boolean | undefined {
417+ return syncRead < boolean > ( "isVisitorEngaged" )
418+ }
419+
420+ export function on ( event : TawkEventName , listener : ( payload ?: unknown ) => void ) : ( ) => void {
421+ let set = eventListeners . get ( event )
422+ if ( ! set ) {
423+ set = new Set ( )
424+ eventListeners . set ( event , set )
425+ }
426+ set . add ( listener )
427+ return ( ) => set ?. delete ( listener )
213428}
214429
215430export async function destroy ( ) : Promise < void > {
@@ -222,6 +437,7 @@ export async function destroy(): Promise<void> {
222437 queue . reset ( )
223438 store . reset ( )
224439 unreadListeners . clear ( )
440+ eventListeners . clear ( )
225441 readyPromise = undefined
226442 readyResolve = undefined
227443 lifecycle . clearConfigHash ( )
0 commit comments