mirror of
https://github.com/misode/misode.github.io.git
synced 2026-05-05 15:11:46 +00:00
Add GA4 events
This commit is contained in:
Generated
+13
@@ -38,6 +38,7 @@
|
||||
"@rollup/plugin-alias": "^3.1.9",
|
||||
"@rollup/plugin-html": "^0.2.3",
|
||||
"@types/google.analytics": "0.0.40",
|
||||
"@types/gtag.js": "^0.0.10",
|
||||
"@types/howler": "^2.2.4",
|
||||
"@types/js-yaml": "^4.0.4",
|
||||
"@types/lz-string": "^1.3.34",
|
||||
@@ -594,6 +595,12 @@
|
||||
"integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/gtag.js": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.10.tgz",
|
||||
"integrity": "sha512-98Hy7woUb3jMAMXkZQwfIOYNyfxmI0+U4m0PpCGdnd/FHk0tDpQFCqgXdNkdEoXsKkcGya/2Gew1cAJjKJspVw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/howler": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.4.tgz",
|
||||
@@ -5584,6 +5591,12 @@
|
||||
"integrity": "sha512-R3HpnLkqmKxhUAf8kIVvDVGJqPtaaZlW4yowNwjOZUTmYUQEgHh8Nh5wkSXKMroNAuQM8gbXJHmNbbgA8tdb7Q==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/gtag.js": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/gtag.js/-/gtag.js-0.0.10.tgz",
|
||||
"integrity": "sha512-98Hy7woUb3jMAMXkZQwfIOYNyfxmI0+U4m0PpCGdnd/FHk0tDpQFCqgXdNkdEoXsKkcGya/2Gew1cAJjKJspVw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/howler": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/howler/-/howler-2.2.4.tgz",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"@rollup/plugin-alias": "^3.1.9",
|
||||
"@rollup/plugin-html": "^0.2.3",
|
||||
"@types/google.analytics": "0.0.40",
|
||||
"@types/gtag.js": "^0.0.10",
|
||||
"@types/howler": "^2.2.4",
|
||||
"@types/js-yaml": "^4.0.4",
|
||||
"@types/lz-string": "^1.3.34",
|
||||
|
||||
+155
-9
@@ -1,4 +1,10 @@
|
||||
import type { VersionId } from './services'
|
||||
|
||||
type Method = 'menu' | 'hotkey'
|
||||
|
||||
export namespace Analytics {
|
||||
|
||||
/** Universal Analytics */
|
||||
const ID_SITE = 'Site'
|
||||
const ID_GENERATOR = 'Generator'
|
||||
|
||||
@@ -22,19 +28,40 @@ export namespace Analytics {
|
||||
ga('send', 'pageview')
|
||||
}
|
||||
|
||||
export function setLanguage(language: string) {
|
||||
dimension(DIM_LANGUAGE, language)
|
||||
event(ID_SITE, 'set-language', language)
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
export function generatorEvent(action: string, label?: string) {
|
||||
event(ID_GENERATOR, action, label)
|
||||
}
|
||||
|
||||
function legacyMethod(method: Method) {
|
||||
return method === 'menu' ? 'Menu' : 'Hotkey'
|
||||
}
|
||||
/** END Universal Analytics 4 */
|
||||
|
||||
export function setLocale(locale: string) {
|
||||
dimension(DIM_LANGUAGE, locale)
|
||||
event(ID_SITE, 'set-language', locale)
|
||||
gtag('event', 'use_locale', {
|
||||
locale,
|
||||
})
|
||||
}
|
||||
|
||||
export function setTheme(theme: string) {
|
||||
dimension(DIM_THEME, theme)
|
||||
event(ID_SITE, 'set-theme', theme)
|
||||
gtag('event', 'use_theme', {
|
||||
theme,
|
||||
})
|
||||
}
|
||||
|
||||
export function setVersion(version: string) {
|
||||
dimension(DIM_VERSION, version)
|
||||
event(ID_GENERATOR, 'set-version', version)
|
||||
gtag('event', 'use_version', {
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
export function setPreview(preview: string) {
|
||||
@@ -42,15 +69,134 @@ export namespace Analytics {
|
||||
event(ID_GENERATOR, 'set-preview', preview)
|
||||
}
|
||||
|
||||
export function setGenerator(generator: string) {
|
||||
dimension(DIM_GENERATOR, generator)
|
||||
export function setGenerator(file_type: string) {
|
||||
dimension(DIM_GENERATOR, file_type)
|
||||
gtag('event', 'use_generator', {
|
||||
file_type,
|
||||
})
|
||||
}
|
||||
|
||||
export function setPrefersColorScheme(colorScheme: string) {
|
||||
dimension(DIM_PREFERS_COLOR_SCHEME, colorScheme)
|
||||
export function setPrefersColorScheme(color_scheme: string) {
|
||||
dimension(DIM_PREFERS_COLOR_SCHEME, color_scheme)
|
||||
gtag('event', 'prefers_color_scheme', {
|
||||
color_scheme,
|
||||
})
|
||||
}
|
||||
|
||||
export function generatorEvent(action: string, label?: string) {
|
||||
event(ID_GENERATOR, action, label)
|
||||
export function resetGenerator(file_type: string, history: number, method: Method) {
|
||||
event(ID_GENERATOR, 'reset')
|
||||
gtag('event', 'reset_generator', {
|
||||
file_type,
|
||||
history,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function undoGenerator(file_type: string, history: number, method: Method) {
|
||||
event(ID_GENERATOR, 'undo', legacyMethod(method))
|
||||
gtag('event', 'undo_generator', {
|
||||
file_type,
|
||||
history,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function redoGenerator(file_type: string, history: number, method: Method) {
|
||||
event(ID_GENERATOR, 'undo', legacyMethod(method))
|
||||
gtag('event', 'redo_generator', {
|
||||
file_type,
|
||||
history,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function saveProjectFile(file_type: string, project_size: number, projects_count: number, method: Method) {
|
||||
event(ID_GENERATOR, 'save-project-file', legacyMethod(method))
|
||||
gtag('event', 'save_project_file', {
|
||||
file_type,
|
||||
project_size,
|
||||
projects_count,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function loadPreset(file_type: string, file_name: string) {
|
||||
event(ID_GENERATOR, 'load-preset', file_name)
|
||||
gtag('event', 'load_generator_preset', {
|
||||
file_type,
|
||||
file_name,
|
||||
})
|
||||
}
|
||||
|
||||
export function openPreset(file_type: string, file_name: string) {
|
||||
gtag('event', 'open_generator_preset', {
|
||||
file_type,
|
||||
file_name,
|
||||
})
|
||||
}
|
||||
|
||||
export function createSnippet(file_type: string, snippet_id: string, version: VersionId, data_size: number, compressed_size: number, compression_rate: number) {
|
||||
gtag('event', 'create_generator_snippet', {
|
||||
file_type,
|
||||
snippet_id,
|
||||
version,
|
||||
data_size,
|
||||
compressed_size,
|
||||
compression_rate,
|
||||
})
|
||||
}
|
||||
|
||||
export function openSnippet(file_type: string, snippet_id: string, version: VersionId) {
|
||||
gtag('event', 'open_generator_snippet', {
|
||||
file_type,
|
||||
snippet_id,
|
||||
version,
|
||||
})
|
||||
}
|
||||
|
||||
export function copyOutput(file_type: string, method: Method) {
|
||||
gtag('event', 'copy_generator_output', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function downloadOutput(file_type: string, method: Method) {
|
||||
gtag('event', 'download_generator_output', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function showOutput(file_type: string, method: Method) {
|
||||
event(ID_GENERATOR, 'toggle-output', 'visible')
|
||||
gtag('event', 'show_generator_output', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function hideOutput(file_type: string, method: Method) {
|
||||
event(ID_GENERATOR, 'toggle-output', 'hidden')
|
||||
gtag('event', 'hide_generator_output', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function showPreview(file_type: string, method: Method) {
|
||||
event(ID_GENERATOR, 'toggle-preview', 'visible')
|
||||
gtag('event', 'show_generator_preview', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
|
||||
export function hidePreview(file_type: string, method: Method) {
|
||||
event(ID_GENERATOR, 'toggle-preview', 'hidden')
|
||||
gtag('event', 'hide_generator_preview', {
|
||||
file_type,
|
||||
method,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ const Themes: Record<string, keyof typeof Octicon> = {
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
const { lang, locale, changeLanguage } = useLocale()
|
||||
const { lang, locale, changeLocale: changeLanguage } = useLocale()
|
||||
const { theme, changeTheme } = useTheme()
|
||||
const { version } = useVersion()
|
||||
const { title } = useTitle()
|
||||
|
||||
@@ -9,12 +9,12 @@ import { Store } from '../Store'
|
||||
interface Locale {
|
||||
lang: string,
|
||||
locale: (key: string, ...params: string[]) => string,
|
||||
changeLanguage: (lang: string) => unknown,
|
||||
changeLocale: (lang: string) => unknown,
|
||||
}
|
||||
const Locale = createContext<Locale>({
|
||||
lang: 'none',
|
||||
locale: key => key,
|
||||
changeLanguage: () => {},
|
||||
changeLocale: () => {},
|
||||
})
|
||||
|
||||
export const Locales: {
|
||||
@@ -59,9 +59,9 @@ export function LocaleProvider({ children }: { children: ComponentChildren }) {
|
||||
return localize(lang, key, ...params)
|
||||
}, [lang])
|
||||
|
||||
const changeLanguage = useCallback(async (lang: string) => {
|
||||
const changeLocale = useCallback(async (lang: string) => {
|
||||
await loadLocale(lang)
|
||||
Analytics.setLanguage(lang)
|
||||
Analytics.setLocale(lang)
|
||||
Store.setLanguage(lang)
|
||||
setLanguage(lang)
|
||||
}, [])
|
||||
@@ -79,8 +79,8 @@ export function LocaleProvider({ children }: { children: ComponentChildren }) {
|
||||
|
||||
const value: Locale = {
|
||||
lang,
|
||||
locale: locale,
|
||||
changeLanguage,
|
||||
locale,
|
||||
changeLocale,
|
||||
}
|
||||
|
||||
return <Locale.Provider value={value}>
|
||||
|
||||
@@ -26,6 +26,7 @@ export type ProjectFile = {
|
||||
}
|
||||
|
||||
interface ProjectContext {
|
||||
projects: Project[],
|
||||
project: Project,
|
||||
file?: ProjectFile,
|
||||
changeProject: (name: string) => unknown,
|
||||
@@ -35,6 +36,7 @@ interface ProjectContext {
|
||||
closeFile: () => unknown,
|
||||
}
|
||||
const Project = createContext<ProjectContext>({
|
||||
projects: [DRAFT_PROJECT],
|
||||
project: DRAFT_PROJECT,
|
||||
changeProject: () => {},
|
||||
updateProject: () => {},
|
||||
@@ -105,6 +107,7 @@ export function ProjectProvider({ children }: { children: ComponentChildren }) {
|
||||
}, [])
|
||||
|
||||
const value: ProjectContext = {
|
||||
projects,
|
||||
project,
|
||||
file,
|
||||
changeProject: setProjectName,
|
||||
|
||||
+27
-14
@@ -8,17 +8,19 @@ import { useLocale, useProject, useTitle, useVersion } from '../contexts'
|
||||
import { AsyncCancel, useActiveTimeout, useAsync, useModel, useSearchParam } from '../hooks'
|
||||
import { getOutput } from '../schema/transformOutput'
|
||||
import type { VersionId } from '../services'
|
||||
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet, SHARE_KEY } from '../services'
|
||||
import { checkVersion, fetchPreset, getBlockStates, getCollections, getModel, getSnippet, shareSnippet } from '../services'
|
||||
import { Store } from '../Store'
|
||||
import { cleanUrl, deepEqual, getGenerator } from '../Utils'
|
||||
|
||||
export const SHARE_KEY = 'share'
|
||||
|
||||
interface Props {
|
||||
default?: true,
|
||||
}
|
||||
export function Generator({}: Props) {
|
||||
const { locale } = useLocale()
|
||||
const { version, changeVersion, changeTargetVersion } = useVersion()
|
||||
const { project, file, updateFile, openFile, closeFile } = useProject()
|
||||
const { projects, project, file, updateFile, openFile, closeFile } = useProject()
|
||||
const [error, setError] = useState<Error | string | null>(null)
|
||||
const [errorBoundary, errorRetry] = useErrorBoundary()
|
||||
if (errorBoundary) {
|
||||
@@ -87,6 +89,7 @@ export function Generator({}: Props) {
|
||||
setPreviewShown(true)
|
||||
setSourceShown(false)
|
||||
}
|
||||
Analytics.openSnippet(gen.id, sharedSnippetId, version)
|
||||
data = snippet.data
|
||||
}
|
||||
const [model, blockStates] = await Promise.all([
|
||||
@@ -160,26 +163,26 @@ export function Generator({}: Props) {
|
||||
}, [file, model])
|
||||
|
||||
const reset = () => {
|
||||
Analytics.generatorEvent('reset')
|
||||
Analytics.resetGenerator(gen.id, model?.historyIndex ?? 1, 'menu')
|
||||
model?.reset(DataModel.wrapLists(model.schema.default()), true)
|
||||
}
|
||||
const undo = (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
Analytics.generatorEvent('undo', 'Menu')
|
||||
Analytics.undoGenerator(gen.id, (model?.historyIndex ?? 1) - 1, 'menu')
|
||||
model?.undo()
|
||||
}
|
||||
const redo = (e: MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
Analytics.generatorEvent('redo', 'Menu')
|
||||
Analytics.redoGenerator(gen.id, (model?.historyIndex ?? 1) + 1, 'menu')
|
||||
model?.redo()
|
||||
}
|
||||
|
||||
const onKeyUp = (e: KeyboardEvent) => {
|
||||
if (e.ctrlKey && e.key === 'z') {
|
||||
Analytics.generatorEvent('undo', 'Hotkey')
|
||||
Analytics.undoGenerator(gen.id, (model?.historyIndex ?? 1) - 1, 'hotkey')
|
||||
model?.undo()
|
||||
} else if (e.ctrlKey && e.key === 'y') {
|
||||
Analytics.generatorEvent('redo', 'Hotkey')
|
||||
Analytics.redoGenerator(gen.id, (model?.historyIndex ?? 1) + 1, 'hotkey')
|
||||
model?.redo()
|
||||
}
|
||||
}
|
||||
@@ -187,7 +190,7 @@ export function Generator({}: Props) {
|
||||
if (e.ctrlKey && e.key === 's') {
|
||||
e.preventDefault()
|
||||
if (model && blockStates && file) {
|
||||
Analytics.generatorEvent('save', 'Hotkey')
|
||||
Analytics.saveProjectFile(gen.id, project.files.length, projects.length, 'hotkey')
|
||||
const data = getOutput(model, blockStates)
|
||||
updateFile(gen.id, file?.id, { id: file?.id, data })
|
||||
setDirty(false)
|
||||
@@ -213,7 +216,7 @@ export function Generator({}: Props) {
|
||||
}, [version, gen.id])
|
||||
|
||||
const selectPreset = (id: string) => {
|
||||
Analytics.generatorEvent('load-preset', id)
|
||||
Analytics.loadPreset(gen.id, id)
|
||||
setSharedSnippetId(undefined, true)
|
||||
changeTargetVersion(version, true)
|
||||
setCurrentPreset(id)
|
||||
@@ -260,7 +263,9 @@ export function Generator({}: Props) {
|
||||
setShareShown(true)
|
||||
} else {
|
||||
shareSnippet(gen.id, version, output, previewShown)
|
||||
.then(url => {
|
||||
.then(({ id, length, compressed, rate }) => {
|
||||
Analytics.createSnippet(gen.id, id, version, length, compressed, rate)
|
||||
const url = `${location.origin}/${gen.url}/?${SHARE_KEY}=${id}`
|
||||
setShareUrl(url)
|
||||
setShareShown(true)
|
||||
})
|
||||
@@ -289,11 +294,11 @@ export function Generator({}: Props) {
|
||||
const [doImport, setImport] = useState(0)
|
||||
|
||||
const copySource = () => {
|
||||
Analytics.generatorEvent('copy')
|
||||
Analytics.copyOutput(gen.id, 'menu')
|
||||
setCopy(doCopy + 1)
|
||||
}
|
||||
const downloadSource = () => {
|
||||
Analytics.generatorEvent('download')
|
||||
Analytics.downloadOutput(gen.id, 'menu')
|
||||
setDownload(doDownload + 1)
|
||||
}
|
||||
const importSource = () => {
|
||||
@@ -302,7 +307,11 @@ export function Generator({}: Props) {
|
||||
setImport(doImport + 1)
|
||||
}
|
||||
const toggleSource = () => {
|
||||
Analytics.generatorEvent('toggle-output', !sourceShown ? 'visible' : 'hidden')
|
||||
if (sourceShown) {
|
||||
Analytics.hideOutput(gen.id, 'menu')
|
||||
} else {
|
||||
Analytics.showOutput(gen.id, 'menu')
|
||||
}
|
||||
setSourceShown(!sourceShown)
|
||||
setCopy(0)
|
||||
setDownload(0)
|
||||
@@ -319,7 +328,11 @@ export function Generator({}: Props) {
|
||||
if (sourceShown) actionsShown += 2
|
||||
|
||||
const togglePreview = () => {
|
||||
Analytics.generatorEvent('toggle-preview', !previewShown ? 'visible' : 'hidden')
|
||||
if (sourceShown) {
|
||||
Analytics.hidePreview(gen.id, 'menu')
|
||||
} else {
|
||||
Analytics.showPreview(gen.id, 'menu')
|
||||
}
|
||||
setPreviewShown(!previewShown)
|
||||
if (!previewShown && sourceShown) {
|
||||
setSourceShown(false)
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import lz from 'lz-string'
|
||||
import config from '../../config.json'
|
||||
import type { VersionId } from './Schemas'
|
||||
|
||||
const API_PREFIX = 'https://z15g7can.directus.app/items'
|
||||
export const SHARE_KEY = 'share'
|
||||
|
||||
const ShareCache = new Map<string, string>()
|
||||
|
||||
export async function shareSnippet(type: string, version: VersionId, jsonData: any, show_preview: boolean) {
|
||||
try {
|
||||
const data = lz.compressToBase64(JSON.stringify(jsonData))
|
||||
const raw = btoa(JSON.stringify(jsonData))
|
||||
console.log('Compression rate', raw.length / data.length)
|
||||
const raw = JSON.stringify(jsonData)
|
||||
const data = lz.compressToBase64(raw)
|
||||
console.log('Compression rate', raw.length / raw.length)
|
||||
const body = JSON.stringify({ data, type, version, show_preview })
|
||||
let id = ShareCache.get(body)
|
||||
if (!id) {
|
||||
@@ -19,8 +17,7 @@ export async function shareSnippet(type: string, version: VersionId, jsonData: a
|
||||
ShareCache.set(body, snippet.id)
|
||||
id = snippet.id as string
|
||||
}
|
||||
const gen = config.generators.find(g => g.id === type)!
|
||||
return `${location.protocol}//${location.host}/${gen.url}/?${SHARE_KEY}=${id}`
|
||||
return { id, length: raw.length, compressed: data.length, rate: raw.length / data.length }
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
e.message = `Error creating share link: ${e.message}`
|
||||
|
||||
Reference in New Issue
Block a user