Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion site/src/components/tools/ShareBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const btn =

export function ShareBar({ url, embedUrl, title, slug }: Props) {
const t = useTranslations("tool");
const { copyResult } = useToolEvents(slug);
const { copyResult, share } = useToolEvents(slug);
const [linkCopied, setLinkCopied] = useState(false);
const [resultCopied, setResultCopied] = useState(false);
const [hasResultLink, setHasResultLink] = useState(false);
Expand Down Expand Up @@ -140,11 +140,15 @@ export function ShareBar({ url, embedUrl, title, slug }: Props) {
if (typeof navigator !== "undefined" && navigator.share) {
try {
await navigator.share({ title, url });
// 成功時だけ送る。キャンセルは reject されるのでここには来ない。
share("native");
return;
} catch {
/* user cancelled — fall through */
}
}
// フォールバックは copy() 側が copy_result("link") を送るため share は送らない
// (同一操作を2イベントで数えると共有率が二重計上になる)。
copy(url, setLinkCopied, "link");
}

Expand Down Expand Up @@ -179,6 +183,8 @@ export function ShareBar({ url, embedUrl, title, slug }: Props) {
className={btn}
aria-label={`${t("share")}: ${name}`}
title={name}
// target="_blank" なので現ページは破棄されず、遷移前の同期送出で取りこぼさない。
onClick={() => share(name)}
>
<Icon />
</a>
Expand Down
12 changes: 12 additions & 0 deletions site/src/lib/analytics/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,15 @@ export function trackCopyResult(p: ToolEventParams): void {
export function trackPresetClick(p: ToolEventParams): void {
emit("preset_click", toParams(p));
}

/**
* 共有導線を踏んだとき(ネイティブ共有・SNSリンク)。
*
* copy_result はクリップボードのみを見ており、ShareBar の主要導線
* (navigator.share / X / Facebook / LINE / WhatsApp / Reddit)は
* 一切計測されていなかった。label に共有先を入れて内訳を取る。
* 外部遷移は計測を待たずに起きるので、送出は必ず遷移前に行う。
*/
export function trackShare(p: ToolEventParams): void {
emit("share", toParams(p));
}
11 changes: 9 additions & 2 deletions site/src/lib/analytics/useToolEvents.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { useCallback, useRef } from "react";
import { trackCalculate, trackCopyResult, trackPresetClick } from "./events";
import { trackCalculate, trackCopyResult, trackPresetClick, trackShare } from "./events";

/**
* ツールコンポーネントから GA4 イベントを送るためのフック。
Expand Down Expand Up @@ -33,5 +33,12 @@ export function useToolEvents(slug: string) {
[slug],
);

return { calculate, copyResult, presetClick };
// share は calculate と違い間引かない。共有先(X / LINE / native ...)ごとの
// 内訳を取るのが目的で、同一着地で複数先に共有するのは正当な行動のため。
const share = useCallback(
(label?: string) => trackShare({ tool: slug, ...(label ? { label } : {}) }),
[slug],
);

return { calculate, copyResult, presetClick, share };
}
Loading