Switch to PostHog for app analytics (#2316)

This commit is contained in:
Geometrically 2024-08-27 21:19:07 -07:00 committed by GitHub
parent 38d95b4faf
commit bf16d360af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 222 additions and 199 deletions

View File

@ -21,14 +21,15 @@
"@vintl/vintl": "^4.4.1",
"dayjs": "^1.11.10",
"floating-vue": "^5.2.2",
"mixpanel-browser": "^2.49.0",
"ofetch": "^1.3.4",
"pinia": "^2.1.7",
"vite-svg-loader": "^5.1.0",
"vue": "^3.4.21",
"vue-multiselect": "3.0.0",
"vue-router": "4.3.0",
"vue-virtual-scroller": "v2.0.0-beta.8"
"vue-virtual-scroller": "v2.0.0-beta.8",
"posthog-js": "^1.158.2",
"@sentry/vue": "^8.27.0"
},
"devDependencies": {
"@tauri-apps/cli": "^2.0.0-rc",

View File

@ -17,16 +17,9 @@ import { command_listener, warning_listener } from '@/helpers/events.js'
import { MinimizeIcon, MaximizeIcon } from '@/assets/icons'
import { type } from '@tauri-apps/plugin-os'
import { isDev, getOS } from '@/helpers/utils.js'
import {
mixpanel_track,
mixpanel_init,
mixpanel_opt_out_tracking,
mixpanel_is_loaded,
} from '@/helpers/mixpanel'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { initAnalytics, debugAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { getVersion } from '@tauri-apps/api/app'
import { TauriEvent } from '@tauri-apps/api/event'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { install_from_file } from './helpers/pack'
import { useError } from '@/store/error.js'
@ -90,11 +83,12 @@ async function setupApp() {
themeStore.collapsedNavigation = collapsed_navigation
themeStore.advancedRendering = advanced_rendering
mixpanel_init('014c7d6a336d0efaefe3aca91063748d', { debug: dev, persistence: 'localStorage' })
initAnalytics()
if (!telemetry) {
mixpanel_opt_out_tracking()
optOutAnalytics()
}
mixpanel_track('Launched', { version, dev, onboarded })
if (dev) debugAnalytics()
trackEvent('Launched', { version, dev, onboarded })
if (!dev) document.addEventListener('contextmenu', (event) => event.preventDefault())
@ -137,9 +131,7 @@ const handleClose = async () => {
const router = useRouter()
router.afterEach((to, from, failure) => {
if (mixpanel_is_loaded()) {
mixpanel_track('PageView', { path: to.path, fromPath: from.path, failed: failure })
}
trackEvent('PageView', { path: to.path, fromPath: from.path, failed: failure })
})
const route = useRoute()
const isOnBrowse = computed(() => route.path.startsWith('/browse'))
@ -214,7 +206,7 @@ async function handleCommand(e) {
// RunMRPack should directly install a local mrpack given a path
if (e.path.endsWith('.mrpack')) {
await install_from_file(e.path).catch(handleError)
mixpanel_track('InstanceCreate', {
trackEvent('InstanceCreate', {
source: 'CreationModalFileDrop',
})
}

View File

@ -23,7 +23,7 @@ import { duplicate, kill, remove, run } from '@/helpers/profile.js'
import { useRouter } from 'vue-router'
import { showProfileInFolder } from '@/helpers/utils.js'
import { useTheming } from '@/store/state.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { handleSevereError } from '@/store/error.js'
import { install as installVersion } from '@/store/install.js'
@ -125,14 +125,14 @@ const handleOptionsClick = async (args) => {
await run(args.item.path).catch((err) =>
handleSevereError(err, { profilePath: args.item.path }),
)
mixpanel_track('InstanceStart', {
trackEvent('InstanceStart', {
loader: args.item.loader,
game_version: args.item.game_version,
})
break
case 'stop':
await kill(args.item.path).catch(handleError)
mixpanel_track('InstanceStop', {
trackEvent('InstanceStop', {
loader: args.item.loader,
game_version: args.item.game_version,
})

View File

@ -70,7 +70,7 @@ import {
get_default_user,
} from '@/helpers/auth'
import { handleError } from '@/store/state.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { process_listener } from '@/helpers/events'
import { handleSevereError } from '@/store/error.js'
@ -118,7 +118,7 @@ async function login() {
await refreshValues()
}
mixpanel_track('AccountLogIn')
trackEvent('AccountLogIn')
}
const logout = async (id) => {
@ -130,7 +130,7 @@ const logout = async (id) => {
} else {
emit('change')
}
mixpanel_track('AccountLogOut')
trackEvent('AccountLogOut')
}
let showCard = ref(false)

View File

@ -5,10 +5,10 @@ import { ChatIcon } from '@/assets/icons'
import { ref } from 'vue'
import { login as login_flow, set_default_user } from '@/helpers/auth.js'
import { handleError } from '@/store/notifications.js'
import mixpanel from 'mixpanel-browser'
import { handleSevereError } from '@/store/error.js'
import { cancel_directory_change } from '@/helpers/settings.js'
import { install } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
const errorModal = ref()
const error = ref()
@ -85,7 +85,7 @@ async function loginMinecraft() {
await set_default_user(loggedIn.id).catch(handleError)
}
await mixpanel.track('AccountLogIn')
await trackEvent('AccountLogIn', { source: 'ErrorModal' })
loadingMinecraft.value = false
errorModal.value.hide()
} catch (err) {

View File

@ -9,8 +9,8 @@ import { get_by_profile_path } from '@/helpers/process'
import { process_listener } from '@/helpers/events'
import { handleError } from '@/store/state.js'
import { showProfileInFolder } from '@/helpers/utils.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { handleSevereError } from '@/store/error.js'
import { trackEvent } from '@/helpers/analytics'
const props = defineProps({
instance: {
@ -45,7 +45,7 @@ const play = async (e, context) => {
)
modLoading.value = false
mixpanel_track('InstancePlay', {
trackEvent('InstancePlay', {
loader: props.instance.loader,
game_version: props.instance.game_version,
source: context,
@ -58,7 +58,7 @@ const stop = async (e, context) => {
await kill(props.instance.path).catch(handleError)
mixpanel_track('InstanceStop', {
trackEvent('InstanceStop', {
loader: props.instance.loader,
game_version: props.instance.game_version,
source: context,

View File

@ -216,7 +216,7 @@ import { convertFileSrc } from '@tauri-apps/api/core'
import { get_game_versions, get_loader_versions } from '@/helpers/metadata'
import { handleError } from '@/store/notifications.js'
import Multiselect from 'vue-multiselect'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { useTheming } from '@/store/state.js'
import { listen } from '@tauri-apps/api/event'
import { install_from_file } from '@/helpers/pack.js'
@ -264,13 +264,13 @@ defineExpose({
hide()
if (event.payload && event.payload.length > 0 && event.payload[0].endsWith('.mrpack')) {
await install_from_file(event.payload[0]).catch(handleError)
mixpanel_track('InstanceCreate', {
trackEvent('InstanceCreate', {
source: 'CreationModalFileDrop',
})
}
})
mixpanel_track('InstanceCreateStart', { source: 'CreationModal' })
trackEvent('InstanceCreateStart', { source: 'CreationModal' })
},
})
@ -360,7 +360,7 @@ const create_instance = async () => {
icon.value,
).catch(handleError)
mixpanel_track('InstanceCreate', {
trackEvent('InstanceCreate', {
profile_name: profile_name.value,
game_version: game_version.value,
loader: loader.value,
@ -419,7 +419,7 @@ const openFile = async () => {
hide()
await install_from_file(newProject).catch(handleError)
mixpanel_track('InstanceCreate', {
trackEvent('InstanceCreate', {
source: 'CreationModalFileOpen',
})
}

View File

@ -40,8 +40,8 @@ import { Modal, Button } from '@modrinth/ui'
import { ref } from 'vue'
import { find_filtered_jres } from '@/helpers/jre.js'
import { handleError } from '@/store/notifications.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { useTheming } from '@/store/theme.js'
import { trackEvent } from '@/helpers/analytics'
const themeStore = useTheming()
@ -67,7 +67,7 @@ const emit = defineEmits(['submit'])
function setJavaInstall(javaInstall) {
emit('submit', javaInstall)
detectJavaModal.value.hide()
mixpanel_track('JavaAutoDetect', {
trackEvent('JavaAutoDetect', {
path: javaInstall.path,
version: javaInstall.version,
})

View File

@ -65,8 +65,8 @@ import { auto_install_java, find_filtered_jres, get_jre, test_jre } from '@/help
import { ref } from 'vue'
import { open } from '@tauri-apps/plugin-dialog'
import JavaDetectionModal from '@/components/ui/JavaDetectionModal.vue'
import { mixpanel_track } from '@/helpers/mixpanel'
import { handleError } from '@/store/state.js'
import { trackEvent } from '@/helpers/analytics'
const props = defineProps({
version: {
@ -113,7 +113,7 @@ async function testJava() {
)
testingJava.value = false
mixpanel_track('JavaTest', {
trackEvent('JavaTest', {
path: props.modelValue ? props.modelValue.path : '',
success: testingJavaSuccess.value,
})
@ -136,7 +136,7 @@ async function handleJavaFileInput() {
}
}
mixpanel_track('JavaManualSelect', {
trackEvent('JavaManualSelect', {
path: filePath,
version: props.version,
})
@ -170,7 +170,7 @@ async function reinstallJava() {
}
}
mixpanel_track('JavaReInstall', {
trackEvent('JavaReInstall', {
path: path,
version: props.version,
})

View File

@ -117,9 +117,9 @@ import { useRouter } from 'vue-router'
import { progress_bars_list } from '@/helpers/state.js'
import ProgressBar from '@/components/ui/ProgressBar.vue'
import { handleError } from '@/store/notifications.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { ChatIcon } from '@/assets/icons'
import { get_many } from '@/helpers/profile.js'
import { trackEvent } from '@/helpers/analytics'
const router = useRouter()
const card = ref(null)
@ -164,7 +164,7 @@ const stop = async (process) => {
try {
await killProcess(process.uuid).catch(handleError)
mixpanel_track('InstanceStop', {
trackEvent('InstanceStop', {
loader: process.profile.loader,
game_version: process.profile.game_version,
source: 'AppBar',

View File

@ -61,7 +61,7 @@ import { formatCategory } from '@modrinth/utils'
import { add_project_from_version as installMod } from '@/helpers/profile'
import { ref } from 'vue'
import { handleError, useTheming } from '@/store/state.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
const themeStore = useTheming()
@ -87,7 +87,7 @@ defineExpose({
incompatibleModal.value.show()
mixpanel_track('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
trackEvent('ProjectInstallStart', { source: 'ProjectIncompatibilityWarningModal' })
},
})
@ -98,7 +98,7 @@ const install = async () => {
onInstall.value(selectedVersion.value.id)
incompatibleModal.value.hide()
mixpanel_track('ProjectInstall', {
trackEvent('ProjectInstall', {
loader: instance.value.loader,
game_version: instance.value.game_version,
id: project.value,

View File

@ -3,7 +3,7 @@ import { XIcon, DownloadIcon } from '@modrinth/assets'
import { Button, Modal } from '@modrinth/ui'
import { install as pack_install } from '@/helpers/pack'
import { ref } from 'vue'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { useTheming } from '@/store/theme.js'
import { handleError } from '@/store/state.js'
@ -25,7 +25,7 @@ defineExpose({
onInstall.value = callback
mixpanel_track('PackInstallStart')
trackEvent('PackInstallStart')
},
})
@ -39,7 +39,7 @@ async function install() {
project.value.title,
project.value.icon_url,
).catch(handleError)
mixpanel_track('PackInstall', {
trackEvent('PackInstall', {
id: project.value.id,
version_id: versionId.value,
title: project.value.title,

View File

@ -19,10 +19,10 @@ import {
import { open } from '@tauri-apps/plugin-dialog'
import { installVersionDependencies } from '@/store/install.js'
import { handleError } from '@/store/notifications.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { useTheming } from '@/store/theme.js'
import { useRouter } from 'vue-router'
import { convertFileSrc } from '@tauri-apps/api/core'
import { trackEvent } from '@/helpers/analytics'
const themeStore = useTheming()
const router = useRouter()
@ -88,7 +88,7 @@ defineExpose({
installModal.value.show()
mixpanel_track('ProjectInstallStart', { source: 'ProjectInstallModal' })
trackEvent('ProjectInstallStart', { source: 'ProjectInstallModal' })
},
})
@ -115,7 +115,7 @@ async function install(instance) {
instance.installedMod = true
instance.installing = false
mixpanel_track('ProjectInstall', {
trackEvent('ProjectInstall', {
loader: instance.loader,
game_version: instance.game_version,
id: project.value.id,
@ -137,7 +137,7 @@ const toggleCreation = () => {
loader.value = null
if (showCreation.value) {
mixpanel_track('InstanceCreateStart', { source: 'ProjectInstallModal' })
trackEvent('InstanceCreateStart', { source: 'ProjectInstallModal' })
}
}
@ -186,7 +186,7 @@ const createInstance = async () => {
const instance = await get(id, true)
await installVersionDependencies(instance, versions.value[0])
mixpanel_track('InstanceCreate', {
trackEvent('InstanceCreate', {
profile_name: name.value,
game_version: versions.value[0].game_versions[0],
loader: loader,
@ -195,7 +195,7 @@ const createInstance = async () => {
source: 'ProjectInstallModal',
})
mixpanel_track('ProjectInstall', {
trackEvent('ProjectInstall', {
loader: loader,
game_version: versions.value[0].game_versions[0],
id: project.value,

View File

@ -0,0 +1,23 @@
import { posthog } from 'posthog-js'
export const initAnalytics = () => {
posthog.init('phc_hm2ihMpTAoE86xIm7XzsCB8RPiTRKivViK5biiHedm', {
persistence: 'localStorage',
})
}
export const debugAnalytics = () => {
posthog.debug()
}
export const optOutAnalytics = () => {
posthog.opt_out_capturing()
}
export const optInAnalytics = () => {
posthog.opt_in_capturing()
}
export const trackEvent = (eventName, properties) => {
posthog.capture(eventName, properties)
}

View File

@ -1,57 +0,0 @@
import mixpanel from 'mixpanel-browser'
// mixpanel_track
function trackWrapper(originalTrack) {
return function (event_name, properties = {}) {
try {
originalTrack(event_name, properties)
} catch (e) {
console.error(e)
}
}
}
export const mixpanel_track = trackWrapper(mixpanel.track.bind(mixpanel))
// mixpanel_opt_out_tracking()
function optOutTrackingWrapper(originalOptOutTracking) {
return function () {
try {
originalOptOutTracking()
} catch (e) {
console.error(e)
}
}
}
export const mixpanel_opt_out_tracking = optOutTrackingWrapper(
mixpanel.opt_out_tracking.bind(mixpanel),
)
// mixpanel_opt_in_tracking()
function optInTrackingWrapper(originalOptInTracking) {
return function () {
try {
originalOptInTracking()
} catch (e) {
console.error(e)
}
}
}
export const mixpanel_opt_in_tracking = optInTrackingWrapper(
mixpanel.opt_in_tracking.bind(mixpanel),
)
// mixpanel_init
function initWrapper(originalInit) {
return function (token, config = {}) {
try {
originalInit(token, config)
} catch (e) {
console.error(e)
}
}
}
export const mixpanel_init = initWrapper(mixpanel.init.bind(mixpanel))
export const mixpanel_is_loaded = () => {
return mixpanel.__loaded
}

View File

@ -5,6 +5,7 @@ import { createPinia } from 'pinia'
import FloatingVue from 'floating-vue'
import 'floating-vue/dist/style.css'
import { createPlugin } from '@vintl/vintl/plugin'
import * as Sentry from '@sentry/vue'
const VIntlPlugin = createPlugin({
controllerOpts: {
@ -26,6 +27,14 @@ const VIntlPlugin = createPlugin({
const pinia = createPinia()
let app = createApp(App)
Sentry.init({
app,
dsn: 'https://9508775ee5034536bc70433f5f531dd4@o485889.ingest.us.sentry.io/4504579615227904',
integrations: [Sentry.browserTracingIntegration({ router })],
tracesSampleRate: 0.1,
})
app.use(router)
app.use(pinia)
app.use(FloatingVue)

View File

@ -8,7 +8,7 @@ import { get_java_versions, get_max_memory, set_java_version } from '@/helpers/j
import { get as getCreds, logout } from '@/helpers/mr_auth.js'
import JavaSelector from '@/components/ui/JavaSelector.vue'
import ModrinthLoginScreen from '@/components/ui/tutorial/ModrinthLoginScreen.vue'
import { mixpanel_opt_out_tracking, mixpanel_opt_in_tracking } from '@/helpers/mixpanel'
import { optOutAnalytics, optInAnalytics } from '@/helpers/analytics'
import { open } from '@tauri-apps/plugin-dialog'
import { getOS } from '@/helpers/utils.js'
import { getVersion } from '@tauri-apps/api/app'
@ -45,9 +45,9 @@ watch(
const setSettings = JSON.parse(JSON.stringify(newSettings))
if (setSettings.telemetry) {
mixpanel_opt_out_tracking()
optInAnalytics()
} else {
mixpanel_opt_in_tracking()
optOutAnalytics()
}
setSettings.extra_launch_args = setSettings.launchArgs.trim().split(/\s+/).filter(Boolean)

View File

@ -131,9 +131,8 @@ import { ref, onUnmounted } from 'vue'
import { handleError, useBreadcrumbs, useLoading } from '@/store/state'
import { showProfileInFolder } from '@/helpers/utils.js'
import ContextMenu from '@/components/ui/ContextMenu.vue'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { convertFileSrc } from '@tauri-apps/api/core'
import { useFetch } from '@/helpers/fetch'
import { handleSevereError } from '@/store/error.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import dayjs from 'dayjs'
@ -183,7 +182,7 @@ const startInstance = async (context) => {
}
loading.value = false
mixpanel_track('InstanceStart', {
trackEvent('InstanceStart', {
loader: instance.value.loader,
game_version: instance.value.game_version,
source: context,
@ -220,7 +219,7 @@ const stopInstance = async (context) => {
playing.value = false
await kill(route.params.id).catch(handleError)
mixpanel_track('InstanceStop', {
trackEvent('InstanceStop', {
loader: instance.value.loader,
game_version: instance.value.game_version,
source: context,

View File

@ -379,7 +379,7 @@ import {
update_project,
} from '@/helpers/profile.js'
import { handleError } from '@/store/notifications.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
import { listen } from '@tauri-apps/api/event'
import { highlightModInProfile } from '@/helpers/utils.js'
import { MenuIcon, ToggleIcon, TextInputIcon, AddProjectImage, PackageIcon } from '@/assets/icons'
@ -682,7 +682,7 @@ const updateAll = async () => {
projects.value[project].updating = false
}
mixpanel_track('InstanceUpdateAll', {
trackEvent('InstanceUpdateAll', {
loader: props.instance.loader,
game_version: props.instance.game_version,
count: setProjects.length,
@ -708,7 +708,7 @@ const updateProject = async (mod) => {
mod.version = mod.updateVersion.version_number
mod.updateVersion = null
mixpanel_track('InstanceProjectUpdate', {
trackEvent('InstanceProjectUpdate', {
loader: props.instance.loader,
game_version: props.instance.game_version,
id: mod.id,
@ -735,7 +735,7 @@ const toggleDisableMod = async (mod) => {
.then((newPath) => {
mod.path = newPath
mod.disabled = !mod.disabled
mixpanel_track('InstanceProjectDisable', {
trackEvent('InstanceProjectDisable', {
loader: props.instance.loader,
game_version: props.instance.game_version,
id: mod.id,
@ -756,7 +756,7 @@ const removeMod = async (mod) => {
await remove_project(props.instance.path, mod.path).catch(handleError)
projects.value = projects.value.filter((x) => mod.path !== x.path)
mixpanel_track('InstanceProjectRemove', {
trackEvent('InstanceProjectRemove', {
loader: props.instance.loader,
game_version: props.instance.game_version,
id: mod.id,

View File

@ -546,10 +546,10 @@ import { open } from '@tauri-apps/plugin-dialog'
import { get_loader_versions } from '@/helpers/metadata.js'
import { get_game_versions, get_loaders } from '@/helpers/tags.js'
import { handleError } from '@/store/notifications.js'
import { mixpanel_track } from '@/helpers/mixpanel'
import { useTheming } from '@/store/theme.js'
import { useBreadcrumbs } from '@/store/breadcrumbs'
import ModpackVersionModal from '@/components/ui/ModpackVersionModal.vue'
import { trackEvent } from '@/helpers/analytics'
const breadcrumbs = useBreadcrumbs()
@ -590,7 +590,7 @@ const availableGroups = ref([
async function resetIcon() {
icon.value = null
await edit_icon(props.instance.path, null).catch(handleError)
mixpanel_track('InstanceRemoveIcon')
trackEvent('InstanceRemoveIcon')
}
async function setIcon() {
@ -609,7 +609,7 @@ async function setIcon() {
icon.value = value
await edit_icon(props.instance.path, icon.value).catch(handleError)
mixpanel_track('InstanceSetIcon')
trackEvent('InstanceSetIcon')
}
const globalSettings = await get().catch(handleError)
@ -754,7 +754,7 @@ const repairing = ref(false)
async function duplicateProfile() {
await duplicate(props.instance.path).catch(handleError)
mixpanel_track('InstanceDuplicate', {
trackEvent('InstanceDuplicate', {
loader: props.instance.loader,
game_version: props.instance.game_version,
})
@ -765,7 +765,7 @@ async function repairProfile(force) {
await install(props.instance.path, force).catch(handleError)
repairing.value = false
mixpanel_track('InstanceRepair', {
trackEvent('InstanceRepair', {
loader: props.instance.loader,
game_version: props.instance.game_version,
})
@ -796,7 +796,7 @@ async function repairModpack() {
await update_repair_modrinth(props.instance.path).catch(handleError)
inProgress.value = false
mixpanel_track('InstanceRepair', {
trackEvent('InstanceRepair', {
loader: props.instance.loader,
game_version: props.instance.game_version,
})
@ -808,7 +808,7 @@ async function removeProfile() {
await remove(props.instance.path).catch(handleError)
removing.value = false
mixpanel_track('InstanceRemove', {
trackEvent('InstanceRemove', {
loader: props.instance.loader,
game_version: props.instance.game_version,
})

View File

@ -93,7 +93,7 @@ import {
} from '@modrinth/assets'
import { Button, Card } from '@modrinth/ui'
import { ref } from 'vue'
import { mixpanel_track } from '@/helpers/mixpanel'
import { trackEvent } from '@/helpers/analytics'
const props = defineProps({
project: {
@ -112,7 +112,7 @@ const nextImage = () => {
expandedGalleryIndex.value = 0
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
mixpanel_track('GalleryImageNext', {
trackEvent('GalleryImageNext', {
project_id: props.project.id,
url: expandedGalleryItem.value.url,
})
@ -124,7 +124,7 @@ const previousImage = () => {
expandedGalleryIndex.value = props.project.gallery.length - 1
}
expandedGalleryItem.value = props.project.gallery[expandedGalleryIndex.value]
mixpanel_track('GalleryImagePrevious', {
trackEvent('GalleryImagePrevious', {
project_id: props.project.id,
url: expandedGalleryItem.value,
})
@ -135,7 +135,7 @@ const expandImage = (item, index) => {
expandedGalleryIndex.value = index
zoomedIn.value = false
mixpanel_track('GalleryImageExpand', {
trackEvent('GalleryImageExpand', {
project_id: props.project.id,
url: item.url,
})

View File

@ -10,7 +10,7 @@ import {
import { handleError } from '@/store/notifications.js'
import { get_project, get_version_many } from '@/helpers/cache.js'
import { install as packInstall } from '@/helpers/pack.js'
import { mixpanel_track } from '@/helpers/mixpanel.js'
import { trackEvent } from '@/helpers/analytics.js'
import dayjs from 'dayjs'
export const useInstall = defineStore('installStore', {
@ -51,7 +51,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
if (packs.length === 0 || !packs.find((pack) => pack.linked_data?.project_id === project.id)) {
await packInstall(project.id, version, project.title, project.icon_url).catch(handleError)
mixpanel_track('PackInstall', {
trackEvent('PackInstall', {
id: project.id,
version_id: version,
title: project.title,
@ -107,7 +107,7 @@ export const install = async (projectId, versionId, instancePath, source, callba
await add_project_from_version(instance.path, version.id).catch(handleError)
await installVersionDependencies(instance, version)
mixpanel_track('ProjectInstall', {
trackEvent('ProjectInstall', {
loader: instance.loader,
game_version: instance.game_version,
id: project.id,

View File

@ -23,7 +23,3 @@ export const handleError = (err) => {
})
console.error(err)
}
export const handleMixpanelError = (err) => {
console.error(err)
}

View File

@ -86,7 +86,7 @@
],
"enable": true
},
"csp": "default-src 'self'; connect-src ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://mixpanel.com https://*.mixpanel.com https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src unsafe-inline 'self'"
"csp": "default-src 'self'; connect-src ipc: http://ipc.localhost https://modrinth.com https://*.modrinth.com https://*.posthog.com https://*.sentry.io https://*.cloudflare.com https://api.mclo.gs; font-src https://cdn-raw.modrinth.com/fonts/inter/; img-src tauri: https: data: blob: 'unsafe-inline' asset: https://asset.localhost; script-src https://*.cloudflare.com 'self'; frame-src https://*.cloudflare.com https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'; style-src unsafe-inline 'self'"
}
}
}

View File

@ -109,7 +109,7 @@
<p>This data is used to deliver statistics.</p>
<h3>Usage data</h3>
<p>When you interact with the Modrinth App or the Website, we collect through MixPanel:</p>
<p>When you interact with the Modrinth App or the Website, we collect through PostHog:</p>
<ul>
<li>Your IP address</li>
<li>Your anonymized user ID</li>
@ -150,9 +150,10 @@
<a href="https://www.cloudflare.com/en-gb/gdpr/introduction/"> Cloudflare </a>
</li>
<li><a href="https://sentry.io/trust/privacy/">Sentry</a></li>
<li><a href="https://mixpanel.com/legal/privacy-policy">MixPanel</a></li>
<li><a href="https://posthog.com/privacy">PostHog</a></li>
<li><a href="https://www.beehiiv.com/privacy">BeeHiiv</a></li>
<li><a href="https://www.paypal.com/us/legalhub/privacy-full">PayPal</a></li>
<li><a href="https://stripe.com/privacy">Stripe</a></li>
</ul>
<p>
Data that we specifically collect isn't shared with any other third party. We do not sell any

171
pnpm-lock.yaml generated
View File

@ -47,6 +47,9 @@ importers:
'@modrinth/utils':
specifier: workspace:*
version: link:../../packages/utils
'@sentry/vue':
specifier: ^8.27.0
version: 8.27.0(vue@3.4.31(typescript@5.5.3))
'@tauri-apps/api':
specifier: ^2.0.0-rc.3
version: 2.0.0-rc.3
@ -71,15 +74,15 @@ importers:
floating-vue:
specifier: ^5.2.2
version: 5.2.2(@nuxt/kit@3.12.3)(vue@3.4.31(typescript@5.5.3))
mixpanel-browser:
specifier: ^2.49.0
version: 2.53.0
ofetch:
specifier: ^1.3.4
version: 1.3.4
pinia:
specifier: ^2.1.7
version: 2.1.7(typescript@5.5.3)(vue@3.4.31(typescript@5.5.3))
posthog-js:
specifier: ^1.158.2
version: 1.158.2
vite-svg-loader:
specifier: ^5.1.0
version: 5.1.0(vue@3.4.31(typescript@5.5.3))
@ -1900,8 +1903,43 @@ packages:
cpu: [x64]
os: [win32]
'@rrweb/types@2.0.0-alpha.16':
resolution: {integrity: sha512-E6cACNVsm+NUhn7dzocQoKyXI7BHrHRRm5Ab23yrAzEQ2caWocCEYJhqDlc4KRVJBkQfXZfyWm8+2d0uggFuZg==}
'@sentry-internal/browser-utils@8.27.0':
resolution: {integrity: sha512-YTIwQ1GM1NTRXgN4DvpFSQ2x4pjlqQ0FQAyHW5x2ZYv4z7VmqG4Xkid1P/srQUipECk6nxkebfD4WR19nLsvnQ==}
engines: {node: '>=14.18'}
'@sentry-internal/feedback@8.27.0':
resolution: {integrity: sha512-b71PQc9aK1X9b/SO1DiJlrnAEx4n0MzPZQ/tKd9oRWDyGit6pJWZfQns9r2rvc96kJPMOTxFAa/upXRCkA723A==}
engines: {node: '>=14.18'}
'@sentry-internal/replay-canvas@8.27.0':
resolution: {integrity: sha512-uuEfiWbjwugB9M4KxXxovHYiKRqg/R6U4EF8xM/Ub4laUuEcWsfRp7lQ3MxL3qYojbca8ncIFic2bIoKMPeejA==}
engines: {node: '>=14.18'}
'@sentry-internal/replay@8.27.0':
resolution: {integrity: sha512-Ofucncaon98dvlxte2L//hwuG9yILSxNrTz/PmO0k+HzB9q+oBic4667QF+azWR2qv4oKSWpc+vEovP3hVqveA==}
engines: {node: '>=14.18'}
'@sentry/browser@8.27.0':
resolution: {integrity: sha512-eL1eaHwoYUGkp4mpeYesH6WtCrm+0u9jYCW5Lm0MAeTmpx22BZKEmj0OljuUJXGnJwFbvPDlRjyz6QG11m8kZA==}
engines: {node: '>=14.18'}
'@sentry/core@8.27.0':
resolution: {integrity: sha512-4frlXluHT3Du+Omw91K04jpvbfMtydvg4Bxj2+gt/DT19Swhm/fbEpzdUjgbAd3Jinj/n0qk/jFRXjr9JZKFjg==}
engines: {node: '>=14.18'}
'@sentry/types@8.27.0':
resolution: {integrity: sha512-B6lrP46+m2x0lfqWc9F4VcUbN893mVGnPEd7KIMRk95mPzkFJ3sNxggTQF5/ZfNO7lDQYQb22uysB5sj/BqFiw==}
engines: {node: '>=14.18'}
'@sentry/utils@8.27.0':
resolution: {integrity: sha512-gyJM3SyLQe0A3mkQVVNdKYvk3ZoikkYgyA/D+5StFNLKdyUgEbJgXOGXrQSSYPF7BSX6Sc5b0KHCglPII0KuKw==}
engines: {node: '>=14.18'}
'@sentry/vue@8.27.0':
resolution: {integrity: sha512-kCjrdKCQk9ZgE7HirVaT/hvyBhoryEHickiWQET7fzyEo6Zs7/KoFnNiXzGuZ+XJcZ8R76wlog+awBBmZBuBsQ==}
engines: {node: '>=14.18'}
peerDependencies:
vue: 2.x || 3.x
'@sindresorhus/merge-streams@2.3.0':
resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==}
@ -2056,9 +2094,6 @@ packages:
resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
engines: {node: '>=10.13.0'}
'@types/css-font-loading-module@0.0.7':
resolution: {integrity: sha512-nl09VhutdjINdWyXxHWN/w9zlNCfr60JUqJbd24YXUuCwgeL0TpFSdElCwb6cxfB6ybE19Gjj4g0jsgkXxKv1Q==}
'@types/eslint-scope@3.7.7':
resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
@ -2470,9 +2505,6 @@ packages:
'@webassemblyjs/wast-printer@1.12.1':
resolution: {integrity: sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==}
'@xstate/fsm@1.6.5':
resolution: {integrity: sha512-b5o1I6aLNeYlU/3CPlj/Z91ybk1gUsKT+5NAJI+2W4UjvS5KLG28K9v5UvNoFVjHV8PajVZ00RH3vnjyQO7ZAw==}
'@xtuc/ieee754@1.2.0':
resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
@ -2651,10 +2683,6 @@ packages:
bare-events@2.4.2:
resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==}
base64-arraybuffer@1.0.2:
resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==}
engines: {node: '>= 0.6.0'}
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
@ -4335,9 +4363,6 @@ packages:
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
mixpanel-browser@2.53.0:
resolution: {integrity: sha512-8U7zCTT82yCIH2vfdCvs0ZRWlCgyHMuU4jtC6yOAiNUR4HhnQYk7re/o2GnhfdvYtkPxdda60/3eH1igUlIXuw==}
mkdirp@1.0.4:
resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
engines: {node: '>=10'}
@ -4887,6 +4912,12 @@ packages:
resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==}
engines: {node: ^10 || ^12 || >=14}
posthog-js@1.158.2:
resolution: {integrity: sha512-ovb7GHHRNDf6vmuL+8lbDukewzDzQlLZXg3d475hrfHSBgidYeTxtLGtoBcUz4x6558BLDFjnSip+f3m4rV9LA==}
preact@10.23.2:
resolution: {integrity: sha512-kKYfePf9rzKnxOAKDpsWhg/ysrHPqT+yQ7UW4JjdnqjFIeNUnNcEJvhuA8fDenxAGWzUqtd51DfVg7xp/8T9NA==}
prelude-ls@1.2.1:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
@ -5114,15 +5145,6 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
rrdom@2.0.0-alpha.16:
resolution: {integrity: sha512-m8aoeORWUz7AFdEb7hES7wPeL6fl/oP23RoAlzLXyA/f2+NqCDM7KEyCXY4sHu6CChN3OAUP2BaUGEXn0zynlw==}
rrweb-snapshot@2.0.0-alpha.16:
resolution: {integrity: sha512-p81OrzUiCmUMZzJu4fGHeLB00PIbVIqsV/zhqzr2pitHTUXpMYcyOvDWt0vHdla0vnowEPaHq3Wsu6cUc732/w==}
rrweb@2.0.0-alpha.13:
resolution: {integrity: sha512-a8GXOCnzWHNaVZPa7hsrLZtNZ3CGjiL+YrkpLo0TfmxGLhjNZbWY2r7pE06p+FcjFNlgUVTmFrSJbK3kO7yxvw==}
run-applescript@7.0.0:
resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==}
engines: {node: '>=18'}
@ -5974,6 +5996,9 @@ packages:
resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==}
engines: {node: '>=10.13.0'}
web-vitals@4.2.3:
resolution: {integrity: sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==}
webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@ -7699,9 +7724,60 @@ snapshots:
'@rollup/rollup-win32-x64-msvc@4.18.0':
optional: true
'@rrweb/types@2.0.0-alpha.16':
'@sentry-internal/browser-utils@8.27.0':
dependencies:
rrweb-snapshot: 2.0.0-alpha.16
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry-internal/feedback@8.27.0':
dependencies:
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry-internal/replay-canvas@8.27.0':
dependencies:
'@sentry-internal/replay': 8.27.0
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry-internal/replay@8.27.0':
dependencies:
'@sentry-internal/browser-utils': 8.27.0
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry/browser@8.27.0':
dependencies:
'@sentry-internal/browser-utils': 8.27.0
'@sentry-internal/feedback': 8.27.0
'@sentry-internal/replay': 8.27.0
'@sentry-internal/replay-canvas': 8.27.0
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry/core@8.27.0':
dependencies:
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
'@sentry/types@8.27.0': {}
'@sentry/utils@8.27.0':
dependencies:
'@sentry/types': 8.27.0
'@sentry/vue@8.27.0(vue@3.4.31(typescript@5.5.3))':
dependencies:
'@sentry/browser': 8.27.0
'@sentry/core': 8.27.0
'@sentry/types': 8.27.0
'@sentry/utils': 8.27.0
vue: 3.4.31(typescript@5.5.3)
'@sindresorhus/merge-streams@2.3.0': {}
@ -7811,8 +7887,6 @@ snapshots:
'@trysound/sax@0.2.0': {}
'@types/css-font-loading-module@0.0.7': {}
'@types/eslint-scope@3.7.7':
dependencies:
'@types/eslint': 9.6.0
@ -8475,8 +8549,6 @@ snapshots:
'@xtuc/long': 4.2.2
optional: true
'@xstate/fsm@1.6.5': {}
'@xtuc/ieee754@1.2.0':
optional: true
@ -8688,8 +8760,6 @@ snapshots:
bare-events@2.4.2:
optional: true
base64-arraybuffer@1.0.2: {}
base64-js@1.5.1: {}
binary-extensions@2.3.0: {}
@ -10583,10 +10653,6 @@ snapshots:
mitt@3.0.1: {}
mixpanel-browser@2.53.0:
dependencies:
rrweb: 2.0.0-alpha.13
mkdirp@1.0.4: {}
mlly@1.7.1:
@ -11267,6 +11333,14 @@ snapshots:
picocolors: 1.0.1
source-map-js: 1.2.0
posthog-js@1.158.2:
dependencies:
fflate: 0.4.8
preact: 10.23.2
web-vitals: 4.2.3
preact@10.23.2: {}
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
@ -11468,23 +11542,6 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.18.0
fsevents: 2.3.3
rrdom@2.0.0-alpha.16:
dependencies:
rrweb-snapshot: 2.0.0-alpha.16
rrweb-snapshot@2.0.0-alpha.16: {}
rrweb@2.0.0-alpha.13:
dependencies:
'@rrweb/types': 2.0.0-alpha.16
'@types/css-font-loading-module': 0.0.7
'@xstate/fsm': 1.6.5
base64-arraybuffer: 1.0.2
fflate: 0.4.8
mitt: 3.0.1
rrdom: 2.0.0-alpha.16
rrweb-snapshot: 2.0.0-alpha.16
run-applescript@7.0.0: {}
run-parallel@1.2.0:
@ -12455,6 +12512,8 @@ snapshots:
graceful-fs: 4.2.11
optional: true
web-vitals@4.2.3: {}
webidl-conversions@3.0.1: {}
webpack-sources@3.2.3: {}