使用 Angela 集成 Google Analytics

集成 Google Analytics

extension.html 中添加 Google Analytics 相关的 <script> 即可为 Angela 启用 Google Analytics。

此外,通过 Angela Access Points ,Google Analytics 可以收集细粒度更高的用户行为数据

以下是一个示例实现,将全部代码复制到 extension.html 即可启用进阶 Google Analytics 集成:

<script type="module">
/** @import { Observable } from "rxjs" */
/** @import { User } from "../../../../../angela/src/app/core/user/user.model" */
/** @import { Plan } from "../../../../../angela/src/app/core/plan/plan.model" */

/**
 * @typedef AngelaAccessPoints
 * @property {() => Observable} watchCommands
 * @property {(command) => object} readCommandMetadata
 * @property {() => Observable} watchEvents
 * @property {(event) => object} readEventMetadata
 * @property {() => Observable<string>} queryAuthToken
 * @property {() => Observable<User>} queryUser
 * @property {() => Observable<Plan | null>} queryUserPlan
 */

/**
 * @typedef {((...args: unknown[]) => void) & { adapted?: boolean }} GtagSdk
 */

/**
 * @typedef {AngelaAccessPoints & { gtag?: GtagSdk; }} Global
 */

import * as mRxJS from 'https://cdn.skypack.dev/rxjs@7.8.1';

/**@returns {asserts v is Global} */
function typeAssertGlobal(v) {}
/**@returns {asserts m is typeof import("rxjs")} */
function typeAssertModuleRxJS(m) {}

typeAssertGlobal(window);
const global = window;
const { watchCommands, watchEvents, readCommandMetadata } = global;
typeAssertModuleRxJS(mRxJS);
const { timer, first, map, filter } = mRxJS;

timer(1000, 1000)
  .pipe(
    map(() => global.gtag),
    first(Boolean),
  )
  .subscribe((gtag) => {
    adaptGA(gtag);
    gtag.adapted = true;
  });

/**
 *
 * @param {GtagSdk} gtag
 */
function adaptGA(gtag) {
  const commands$ = watchCommands();
  const events$ = watchEvents();

  commands$
    .pipe(
      filter((command) => {
        const name = command.constructor.name;
        const metadata = readCommandMetadata(command);
        const isProcess = !!metadata.process;
        return isProcess || name === 'Logout';
      }),
    )
    .subscribe((command) => {
      gtag('event', command.constructor.name, { ...command });
    });

  events$
    .pipe(
      filter((event) => {
        const name = event.constructor.name;
        return name === 'ProcessFailed' || name === 'QueryErrored';
      }),
    )
    .subscribe((event) => {
      gtag('event', 'exception', {
        description: `[${event.constructor.name}] ${String(event.error)}`,
        stack: event.error instanceof Error ? event.error.stack : null,
        fatal: false,
      });
    });
}

</script>