mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-09-18 01:19:24 +00:00
Scarf Fire
This commit is contained in:
parent
e59e73ceb0
commit
cd6652a48d
@ -7,7 +7,6 @@ import { ToolWorkflowProvider } from "./contexts/ToolWorkflowContext";
|
|||||||
import { SidebarProvider } from "./contexts/SidebarContext";
|
import { SidebarProvider } from "./contexts/SidebarContext";
|
||||||
import ErrorBoundary from "./components/shared/ErrorBoundary";
|
import ErrorBoundary from "./components/shared/ErrorBoundary";
|
||||||
import HomePage from "./pages/HomePage";
|
import HomePage from "./pages/HomePage";
|
||||||
import { ScarfPixel } from "./components/ScarfPixel";
|
|
||||||
|
|
||||||
// Import global styles
|
// Import global styles
|
||||||
import "./styles/tailwind.css";
|
import "./styles/tailwind.css";
|
||||||
@ -37,7 +36,6 @@ export default function App() {
|
|||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
|
<FileContextProvider enableUrlSync={true} enablePersistence={true}>
|
||||||
<NavigationProvider>
|
<NavigationProvider>
|
||||||
<ScarfPixel />
|
|
||||||
<FilesModalProvider>
|
<FilesModalProvider>
|
||||||
<ToolWorkflowProvider>
|
<ToolWorkflowProvider>
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import { useEffect, useRef } from "react";
|
|
||||||
import { useNavigationState } from "../contexts/NavigationContext";
|
|
||||||
|
|
||||||
export function ScarfPixel() {
|
|
||||||
const { workbench, selectedTool } = useNavigationState();
|
|
||||||
const lastUrlSent = useRef<string | null>(null); // helps with React 18 StrictMode in dev
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Get current pathname from browser location
|
|
||||||
const pathname = window.location.pathname;
|
|
||||||
|
|
||||||
const url = 'https://static.scarf.sh/a.png?x-pxid=3c1d68de-8945-4e9f-873f-65320b6fabf7'
|
|
||||||
+ '&path=' + encodeURIComponent(pathname)
|
|
||||||
+ '&t=' + Date.now(); // cache-buster
|
|
||||||
|
|
||||||
console.log("ScarfPixel: Navigation change", { workbench, selectedTool, pathname });
|
|
||||||
|
|
||||||
if (lastUrlSent.current !== url) {
|
|
||||||
lastUrlSent.current = url;
|
|
||||||
const img = new Image();
|
|
||||||
img.referrerPolicy = "no-referrer-when-downgrade"; // optional
|
|
||||||
img.src = url;
|
|
||||||
|
|
||||||
console.log("ScarfPixel: Fire to... " + pathname, url);
|
|
||||||
}
|
|
||||||
}, [workbench, selectedTool]); // Fire when navigation state changes
|
|
||||||
|
|
||||||
return null; // Nothing visible in UI
|
|
||||||
}
|
|
||||||
|
|
@ -174,7 +174,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
const handleToolSelect = useCallback((toolId: string) => {
|
const handleToolSelect = useCallback((toolId: string) => {
|
||||||
// Set the selected tool and determine the appropriate workbench
|
// Set the selected tool and determine the appropriate workbench
|
||||||
actions.setSelectedTool(toolId);
|
actions.setSelectedTool(toolId);
|
||||||
|
|
||||||
// Get the tool from registry to determine workbench
|
// Get the tool from registry to determine workbench
|
||||||
const tool = getSelectedTool(toolId);
|
const tool = getSelectedTool(toolId);
|
||||||
if (tool && tool.workbench) {
|
if (tool && tool.workbench) {
|
||||||
@ -225,10 +225,7 @@ export function ToolWorkflowProvider({ children }: ToolWorkflowProviderProps) {
|
|||||||
|
|
||||||
// URL sync for proper tool navigation
|
// URL sync for proper tool navigation
|
||||||
useNavigationUrlSync(
|
useNavigationUrlSync(
|
||||||
navigationState.workbench,
|
|
||||||
navigationState.selectedTool,
|
navigationState.selectedTool,
|
||||||
actions.setWorkbench,
|
|
||||||
actions.setSelectedTool,
|
|
||||||
handleToolSelect,
|
handleToolSelect,
|
||||||
() => actions.setSelectedTool(null),
|
() => actions.setSelectedTool(null),
|
||||||
toolRegistry,
|
toolRegistry,
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useCallback } from 'react';
|
import { useEffect, useCallback } from 'react';
|
||||||
import { WorkbenchType, ToolId, ToolRoute } from '../types/navigation';
|
import { WorkbenchType, ToolId } from '../types/navigation';
|
||||||
import { parseToolRoute, updateToolRoute, clearToolRoute } from '../utils/urlRouting';
|
import { parseToolRoute, updateToolRoute, clearToolRoute } from '../utils/urlRouting';
|
||||||
import { ToolRegistry } from '../data/toolsTaxonomy';
|
import { ToolRegistry } from '../data/toolsTaxonomy';
|
||||||
|
|
||||||
@ -11,10 +11,7 @@ import { ToolRegistry } from '../data/toolsTaxonomy';
|
|||||||
* Hook to sync workbench and tool with URL using registry
|
* Hook to sync workbench and tool with URL using registry
|
||||||
*/
|
*/
|
||||||
export function useNavigationUrlSync(
|
export function useNavigationUrlSync(
|
||||||
workbench: WorkbenchType,
|
|
||||||
selectedTool: ToolId | null,
|
selectedTool: ToolId | null,
|
||||||
setWorkbench: (workbench: WorkbenchType) => void,
|
|
||||||
setSelectedTool: (toolId: ToolId | null) => void,
|
|
||||||
handleToolSelect: (toolId: string) => void,
|
handleToolSelect: (toolId: string) => void,
|
||||||
clearToolSelection: () => void,
|
clearToolSelection: () => void,
|
||||||
registry: ToolRegistry,
|
registry: ToolRegistry,
|
||||||
@ -28,7 +25,9 @@ export function useNavigationUrlSync(
|
|||||||
if (route.toolId !== selectedTool) {
|
if (route.toolId !== selectedTool) {
|
||||||
if (route.toolId) {
|
if (route.toolId) {
|
||||||
handleToolSelect(route.toolId);
|
handleToolSelect(route.toolId);
|
||||||
} else {
|
} else if (selectedTool !== null) {
|
||||||
|
// Only clear selection if we actually had a tool selected
|
||||||
|
// Don't clear on initial load when selectedTool starts as null
|
||||||
clearToolSelection();
|
clearToolSelection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -41,8 +40,10 @@ export function useNavigationUrlSync(
|
|||||||
if (selectedTool) {
|
if (selectedTool) {
|
||||||
updateToolRoute(selectedTool, registry);
|
updateToolRoute(selectedTool, registry);
|
||||||
} else {
|
} else {
|
||||||
// Clear URL when no tool is selected
|
// Only clear URL if we're not on the home page already
|
||||||
clearToolRoute();
|
if (window.location.pathname !== '/') {
|
||||||
|
clearToolRoute();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [selectedTool, registry, enableSync]);
|
}, [selectedTool, registry, enableSync]);
|
||||||
|
|
||||||
@ -103,4 +104,4 @@ export function useCurrentRoute(registry: ToolRegistry) {
|
|||||||
}, [registry]);
|
}, [registry]);
|
||||||
|
|
||||||
return getCurrentRoute;
|
return getCurrentRoute;
|
||||||
}
|
}
|
||||||
|
28
frontend/src/utils/scarfTracking.ts
Normal file
28
frontend/src/utils/scarfTracking.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
let lastFiredPathname: string | null = null;
|
||||||
|
let lastFiredTime = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fire scarf pixel for analytics tracking
|
||||||
|
* Only fires if pathname is different from last call or enough time has passed
|
||||||
|
*/
|
||||||
|
export function firePixel(pathname: string): void {
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Only fire if pathname changed or it's been at least 1 second since last fire
|
||||||
|
if (pathname === lastFiredPathname && now - lastFiredTime < 250) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastFiredPathname = pathname;
|
||||||
|
lastFiredTime = now;
|
||||||
|
|
||||||
|
const url = 'https://static.scarf.sh/a.png?x-pxid=3c1d68de-8945-4e9f-873f-65320b6fabf7'
|
||||||
|
+ '&path=' + encodeURIComponent(pathname)
|
||||||
|
|
||||||
|
const img = new Image();
|
||||||
|
img.referrerPolicy = "no-referrer-when-downgrade";
|
||||||
|
img.src = url;
|
||||||
|
|
||||||
|
console.log("ScarfPixel: Fire to... " + pathname);
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import {
|
|||||||
getDefaultWorkbench
|
getDefaultWorkbench
|
||||||
} from '../types/navigation';
|
} from '../types/navigation';
|
||||||
import { ToolRegistry, getToolWorkbench, getToolUrlPath, isValidToolId } from '../data/toolsTaxonomy';
|
import { ToolRegistry, getToolWorkbench, getToolUrlPath, isValidToolId } from '../data/toolsTaxonomy';
|
||||||
|
import { firePixel } from './scarfTracking';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the current URL to extract tool routing information
|
* Parse the current URL to extract tool routing information
|
||||||
@ -44,33 +45,38 @@ export function parseToolRoute(registry: ToolRegistry): ToolRoute {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update URL and fire analytics pixel
|
||||||
|
*/
|
||||||
|
function updateUrl(newPath: string, searchParams: URLSearchParams): void {
|
||||||
|
const currentPath = window.location.pathname;
|
||||||
|
const queryString = searchParams.toString();
|
||||||
|
const fullUrl = newPath + (queryString ? `?${queryString}` : '');
|
||||||
|
|
||||||
|
// Only update URL and fire pixel if something actually changed
|
||||||
|
if (currentPath !== newPath || window.location.search !== (queryString ? `?${queryString}` : '')) {
|
||||||
|
window.history.replaceState(null, '', fullUrl);
|
||||||
|
firePixel(newPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the URL to reflect the current tool selection
|
* Update the URL to reflect the current tool selection
|
||||||
*/
|
*/
|
||||||
export function updateToolRoute(toolId: ToolId, registry: ToolRegistry): void {
|
export function updateToolRoute(toolId: ToolId, registry: ToolRegistry): void {
|
||||||
const currentPath = window.location.pathname;
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
|
||||||
|
|
||||||
const tool = registry[toolId];
|
const tool = registry[toolId];
|
||||||
if (!tool) {
|
if (!tool) {
|
||||||
console.warn(`Tool ${toolId} not found in registry`);
|
console.warn(`Tool ${toolId} not found in registry`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const newPath = getToolUrlPath(toolId, tool);
|
const newPath = getToolUrlPath(toolId, tool);
|
||||||
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
|
|
||||||
// Remove tool query parameter since we're using path-based routing
|
// Remove tool query parameter since we're using path-based routing
|
||||||
searchParams.delete('tool');
|
searchParams.delete('tool');
|
||||||
|
|
||||||
// Construct final URL
|
updateUrl(newPath, searchParams);
|
||||||
const queryString = searchParams.toString();
|
|
||||||
const fullUrl = newPath + (queryString ? `?${queryString}` : '');
|
|
||||||
|
|
||||||
// Update URL without triggering page reload
|
|
||||||
if (currentPath !== newPath || window.location.search !== (queryString ? `?${queryString}` : '')) {
|
|
||||||
window.history.replaceState(null, '', fullUrl);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,10 +86,7 @@ export function clearToolRoute(): void {
|
|||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
searchParams.delete('tool');
|
searchParams.delete('tool');
|
||||||
|
|
||||||
const queryString = searchParams.toString();
|
updateUrl('/', searchParams);
|
||||||
const url = '/' + (queryString ? `?${queryString}` : '');
|
|
||||||
|
|
||||||
window.history.replaceState(null, '', url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user