Skip to content
1 change: 1 addition & 0 deletions apps/app-frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<body>
<div id="app"></div>
<script src="https://tally.so/widgets/embed.js" async></script>
<script type="module" src="/src/main.js"></script>
</body>
</html>
164 changes: 163 additions & 1 deletion apps/app-frontend/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ import RunningAppBar from '@/components/ui/RunningAppBar.vue'
import SplashScreen from '@/components/ui/SplashScreen.vue'
import URLConfirmModal from '@/components/ui/URLConfirmModal.vue'
import { useCheckDisableMouseover } from '@/composables/macCssFix.js'
import { hide_ads_window, init_ads_window } from '@/helpers/ads.js'
import { hide_ads_window, init_ads_window, show_ads_window } from '@/helpers/ads.js'
import { debugAnalytics, initAnalytics, optOutAnalytics, trackEvent } from '@/helpers/analytics'
import { get_user } from '@/helpers/cache.js'
import { command_listener, warning_listener } from '@/helpers/events.js'
import { useFetch } from '@/helpers/fetch.js'
import { cancelLogin, get as getCreds, login, logout } from '@/helpers/mr_auth.js'
import { list } from '@/helpers/profile.js'
import { get } from '@/helpers/settings.ts'
import { get_opening_command, initialize_state } from '@/helpers/state'
import { getOS, isDev, restartApp } from '@/helpers/utils.js'
Expand All @@ -43,6 +44,7 @@ import {
MaximizeIcon,
MinimizeIcon,
NewspaperIcon,
NotepadTextIcon,
PlusIcon,
RestoreIcon,
RightArrowIcon,
Expand All @@ -67,6 +69,7 @@ import { openUrl } from '@tauri-apps/plugin-opener'
import { type } from '@tauri-apps/plugin-os'
import { check } from '@tauri-apps/plugin-updater'
import { saveWindowState, StateFlags } from '@tauri-apps/plugin-window-state'
import { $fetch } from 'ofetch'
import { computed, onMounted, onUnmounted, provide, ref, watch } from 'vue'
import { RouterView, useRoute, useRouter } from 'vue-router'
import { create_profile_and_install_from_file } from './helpers/pack'
Expand All @@ -81,6 +84,7 @@ provideNotificationManager(notificationManager)
const { handleError, addNotification } = notificationManager

const news = ref([])
const availableSurvey = ref(false)

const urlModal = ref(null)

Expand Down Expand Up @@ -225,6 +229,12 @@ async function setupApp() {
} catch (error) {
console.warn('Failed to generate skin previews in app setup.', error)
}

if (osType === 'windows') {
await processPendingSurveys()
} else {
console.info('Skipping user surveys on non-Windows platforms')
}
}

const stateFailed = ref(false)
Expand Down Expand Up @@ -412,6 +422,116 @@ function handleAuxClick(e) {
e.target.dispatchEvent(event)
}
}

function cleanupOldSurveyDisplayData() {
const threeWeeksAgo = new Date()
threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21)

for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)

if (key.startsWith('survey-') && key.endsWith('-display')) {
const dateValue = new Date(localStorage.getItem(key))
if (dateValue < threeWeeksAgo) {
localStorage.removeItem(key)
}
}
}
}

async function openSurvey() {
if (!availableSurvey.value) {
console.error('No survey to open')
return
}

const creds = await getCreds().catch(handleError)
const userId = creds?.user_id

const formId = availableSurvey.value.tally_id

const popupOptions = {
layout: 'modal',
width: 700,
autoClose: 2000,
hideTitle: true,
hiddenFields: {
user_id: userId,
},
onOpen: () => console.info('Opened user survey'),
onClose: () => {
console.info('Closed user survey')
show_ads_window()
},
onSubmit: () => console.info('Active user survey submitted'),
}

try {
hide_ads_window()
if (window.Tally?.openPopup) {
console.info(`Opening Tally popup for user survey (form ID: ${formId})`)
dismissSurvey()
window.Tally.openPopup(formId, popupOptions)
} else {
console.warn('Tally script not yet loaded')
show_ads_window()
}
} catch (e) {
console.error('Error opening Tally popup:', e)
show_ads_window()
}

console.info(`Found user survey to show with tally_id: ${formId}`)
window.Tally.openPopup(formId, popupOptions)
}

function dismissSurvey() {
localStorage.setItem(`survey-${availableSurvey.value.id}-display`, new Date())
availableSurvey.value = undefined
}

async function processPendingSurveys() {
function isWithinLastTwoWeeks(date) {
const twoWeeksAgo = new Date()
twoWeeksAgo.setDate(twoWeeksAgo.getDate() - 14)
return date >= twoWeeksAgo
}

cleanupOldSurveyDisplayData()

const creds = await getCreds().catch(handleError)
const userId = creds?.user_id

const instances = await list().catch(handleError)
const isActivePlayer =
instances.findIndex(
(instance) =>
isWithinLastTwoWeeks(instance.last_played) && !isWithinLastTwoWeeks(instance.created),
) >= 0

let surveys = []
try {
surveys = await $fetch('https://api.modrinth.com/v2/surveys')
} catch (e) {
console.error('Error fetching surveys:', e)
}

const surveyToShow = surveys.find(
(survey) =>
!!(
localStorage.getItem(`survey-${survey.id}-display`) === null &&
survey.type === 'tally_app' &&
((survey.condition === 'active_player' && isActivePlayer) ||
(survey.assigned_users?.includes(userId) && !survey.dismissed_users?.includes(userId)))
),
)

if (surveyToShow) {
availableSurvey.value = surveyToShow
} else {
console.info('No user survey to show')
}
}
</script>

<template>
Expand Down Expand Up @@ -565,6 +685,28 @@ function handleAuxClick(e) {
:class="{ 'sidebar-enabled': sidebarVisible }"
>
<div class="app-viewport flex-grow router-view">
<transition name="popup-survey">
<div
v-if="availableSurvey"
class="w-[400px] z-20 fixed -bottom-12 pb-16 right-[--right-bar-width] mr-4 rounded-t-2xl card-shadow bg-bg-raised border-divider border-[1px] border-solid border-b-0 p-4"
>
<h2 class="text-lg font-extrabold mt-0 mb-2">Hey there Modrinth user!</h2>
<p class="m-0 leading-tight">
Would you mind answering a few questions about your experience with Modrinth App?
</p>
<p class="mt-3 mb-4 leading-tight">
This feedback will go directly to the Modrinth team and help guide future updates!
</p>
<div class="flex gap-2">
<ButtonStyled color="brand">
<button @click="openSurvey"><NotepadTextIcon /> Take survey</button>
</ButtonStyled>
<ButtonStyled>
<button @click="dismissSurvey"><XIcon /> No thanks</button>
</ButtonStyled>
</div>
</div>
</transition>
<div
class="loading-indicator-container h-8 fixed z-50"
:style="{
Expand Down Expand Up @@ -862,6 +1004,26 @@ function handleAuxClick(e) {
.sidebar-teleport-content:empty + .sidebar-default-content.sidebar-enabled {
display: contents;
}

.popup-survey-enter-active {
transition:
opacity 0.25s ease,
transform 0.25s cubic-bezier(0.51, 1.08, 0.35, 1.15);
transform-origin: top center;
}

.popup-survey-leave-active {
transition:
opacity 0.25s ease,
transform 0.25s cubic-bezier(0.68, -0.17, 0.23, 0.11);
transform-origin: top center;
}

.popup-survey-enter-from,
.popup-survey-leave-to {
opacity: 0;
transform: translateY(10rem) scale(0.8) scaleY(1.6);
}
</style>
<style>
.mac {
Expand Down
4 changes: 2 additions & 2 deletions apps/app/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@
"font-src": ["https://cdn-raw.modrinth.com/fonts/"],
"img-src": "https: 'unsafe-inline' 'self' asset: http://asset.localhost http://textures.minecraft.net blob: data:",
"style-src": "'unsafe-inline' 'self'",
"script-src": "https://*.posthog.com 'self'",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com 'self'",
"script-src": "https://*.posthog.com https://tally.so/widgets/embed.js 'self'",
"frame-src": "https://www.youtube.com https://www.youtube-nocookie.com https://discord.com https://tally.so/popup/ 'self'",
"media-src": "https://*.githubusercontent.com"
}
}
Expand Down
2 changes: 2 additions & 0 deletions packages/assets/generated-icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ import _MoreHorizontalIcon from './icons/more-horizontal.svg?component'
import _MoreVerticalIcon from './icons/more-vertical.svg?component'
import _NewspaperIcon from './icons/newspaper.svg?component'
import _NoSignalIcon from './icons/no-signal.svg?component'
import _NotepadTextIcon from './icons/notepad-text.svg?component'
import _OmorphiaIcon from './icons/omorphia.svg?component'
import _OrganizationIcon from './icons/organization.svg?component'
import _PackageClosedIcon from './icons/package-closed.svg?component'
Expand Down Expand Up @@ -315,6 +316,7 @@ export const MoreHorizontalIcon = _MoreHorizontalIcon
export const MoreVerticalIcon = _MoreVerticalIcon
export const NewspaperIcon = _NewspaperIcon
export const NoSignalIcon = _NoSignalIcon
export const NotepadTextIcon = _NotepadTextIcon
export const OmorphiaIcon = _OmorphiaIcon
export const OrganizationIcon = _OrganizationIcon
export const PackageClosedIcon = _PackageClosedIcon
Expand Down
1 change: 1 addition & 0 deletions packages/assets/icons/notepad-text.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.