Run custom js on doc load for usage logging

Is there a way (pack or otherwise) to run custom js code on doc load? I’m trying to implement usage logging for a non-public doc, so I would like to capture clicks and stream the data to a cloud service. I’ve already considered the audit logs and page analytics available in the enterprise plan, but they don’t have the level of detail I’m looking for.

If there are other ways / ideas for granular usage logging, I’m open to those as well.

Gosh. None that I’m directly aware of - for some reason my mind tells me @Bill_French May know some wild tricks

General answer: you can’t do that.

Detailed answer: there’s no way to inject any code into Coda. You can have a tracking pixel, an embed, or a custom pack formula that you would then call in different places — and you can track button clicks with those custom actions (but you’ll have to edit every button and they all will start lagging). But no way to track cell edits, comment writing, content expanding, page scrolling etc. All of these would be sandboxed containers and have no access to the parent page (i.e. the one with the Coda doc).

And even if there were the way it would probably violate Coda Terms of Service.

And even if it didn’t, it may create some privacy issues in the margins between say - GDPR and Coda.

Agree. But, this post contains some speculation concerning Coda’s underlying event handlers and how such access might make real-time [arbitrary] analytics more possible.

This is entirely possible depending on your definition of “click”. As @Paul_Danyliuk indicated, certain objects can be sensitized to capture knowledge about document activity, but it’s a tedious pathway to such knowledge.

I believe the Itsy() Pack can do this and oddly, @Paul demonstrated part of this approach here. I have used this approach to generate analytics but only for my internal users at my company.

Thanks @Bill_French for the Itsy pack idea. Capturing analytics for internal users at my company is exactly what I’m trying to. When I run the code directly in the console, it works, but when I try to use it in an Itsy formula , I’m getting “Could not find a DOM node for the provided slate node”. I can get hello world examples to work with Itsy, but the code I’d actually like to use isn’t working. Please forgive my lack of web development experience, but any ideas?

Which service are you using to track? I think you can implement something using tracking pixels on each page, just need to double check if Coda preloads the pages running the formulas or if it runs only when in view.

I was trying to use a custom script to capture the web socket message relating to the cursor position to record which canvas ids are being visited. I’m open to other ideas though. Is there a third-party tracking service you recommend that I can use through Itsy?

Update: I’m able to get the code to run (as measured by console.log statements), but it’s not doing anything. When running js via Itsy, is it sandboxed away from the page containing it?

Yes because it is a separate html embedded in Coda. Segment has an API for tracking pixels that you can use, you will need to add one to each page you want to track and you can pass different parameters to be stored.

Thanks. I’ll look into the Segment functionality.

1 Like

Yes, as @Leandro_Zubrezki points out, this is like a little app running on an island but inside the visual spectrum of the user.

To make the code do something, it has to engage in communication. @Paul did exactly this with the referenced calculator app. It calls back into the same Coda document with a simple webhook. Such a call could easily send data to another endpoint.

Difficult to comment without seeing the actual code. :wink:

Sorry, I should have included the code. Here it is:

function listen(fn){
  fn=fn||console.log
  let property=Object.getOwnPropertyDescriptor
  (MessageEvent.prototype,"data")
  const data=property.get
  function lookAtMessage(){
    let socket=this.currentTarget instanceof WebSocket
    if(!socket){return data.call(this)}
    let msg=data.call(this)
    Object.defineProperty(this,"data",{value:msg})
    fn({data:msg,socket:this.currentTarget,event:this})
    return msg
  }
  property.get=lookAtMessage
  Object.defineProperty
  (MessageEvent.prototype,"data",property)
}
listen( ({data})=>console.log(data) )

When I run it in the console, it prints the web socket message, which includes the cursor updates with each click. My end goal is to send it to azure event hub instead of the console.

Right, but I think the issue is that in sandboxed “apps” such as those within an embed(), the DOM is sanitized from the code which is the essence of the sandboxing that Coda performs for security reasons.