You've already forked AstralRinth
forked from didirus/AstralRinth
App surveys (#3605)
* feat: surveys * make assigned and dismissed users fields optional * fix: set required CSP sources for Tally forms to show up * make only attempt on windows, temp bypass requirements * fix: lint issues * Add prompt for survey prior to popup * lint * hide ads when survey is open --------- Signed-off-by: Cal H. <hendersoncal117@gmail.com> Co-authored-by: Prospector <prospectordev@gmail.com> Co-authored-by: Prospector <6166773+Prospector@users.noreply.github.com> Co-authored-by: Cal H. <hendersoncal117@gmail.com> Co-authored-by: IMB11 <calum@modrinth.com>
This commit is contained in:
committed by
GitHub
parent
5ffcc48d75
commit
0bc6502443
@@ -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>
|
||||
|
||||
@@ -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'
|
||||
@@ -43,6 +44,7 @@ import {
|
||||
MaximizeIcon,
|
||||
MinimizeIcon,
|
||||
NewspaperIcon,
|
||||
NotepadTextIcon,
|
||||
PlusIcon,
|
||||
RestoreIcon,
|
||||
RightArrowIcon,
|
||||
@@ -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'
|
||||
@@ -81,6 +84,7 @@ provideNotificationManager(notificationManager)
|
||||
const { handleError, addNotification } = notificationManager
|
||||
|
||||
const news = ref([])
|
||||
const availableSurvey = ref(false)
|
||||
|
||||
const urlModal = ref(null)
|
||||
|
||||
@@ -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)
|
||||
@@ -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>
|
||||
@@ -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="{
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
@@ -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
|
||||
|
||||
1
packages/assets/icons/notepad-text.svg
Normal file
1
packages/assets/icons/notepad-text.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-notepad-text-icon lucide-notepad-text"><path d="M8 2v4"/><path d="M12 2v4"/><path d="M16 2v4"/><rect width="16" height="18" x="4" y="4" rx="2"/><path d="M8 10h6"/><path d="M8 14h8"/><path d="M8 18h5"/></svg>
|
||||
|
After Width: | Height: | Size: 409 B |
Reference in New Issue
Block a user