You've already forked AstralRinth
forked from didirus/AstralRinth
Migrate to Nuxt 3 (#933)
* Migrate to Nuxt 3 * Update vercel config * remove tsconfig comment * Changelog experiment + working proj pages * Fix package json * Prevent vercel complaining * fix deploy (hopefully) * Tag generator * Switch to yarn * Vercel pls 🙏 * Fix tag generation bug * Make (most) non-logged in pages work * fix base build * Linting + state * Eradicate axios, make most user pages work * Fix checkbox state being set incorrectly * Make most things work * Final stretch * Finish (most) things * Move to update model value * Fix modal text getting blurred from transforms (#964) * Adjust nav-link border radius when focused (#961) * Transition between animation states on TextLogo (#955) * Transition between animation states on TextLogo * Remove unused refs * Fixes from review * Disable tabbing to pagination arrows when disabled (#972) * Make position of the "no results" text on grid/gallery views consistent (fixes #963) (#965) * Fix position of the "no results" text on grid view * fix padding * Remove extra margin on main page, fixes #957 (#959) * Fix layout shift and placeholder line height (#973) * Fix a lot of issues * Fix more nuxt 3 issues * fix not all versions showing up (temp) * inline inter css file * More nuxt 3 fixes * [skip ci] broken- backup changes * Change modpack warnings to blue instead of red (#991) * Fix some hydration issues * Update nuxt * Fix some images not showing * Add pagination to versions page + fix lag * Make changelog page consistent with versions page * sync before merge * Delete old file * Fix actions failing * update branch * Fixes navbar transition animation. (#1012) * Fixes navbar transition animation. * Fixes Y-axis animation. Fixes mobile menu. Removes highlightjs prop. * Changes xss call to renderString. * Fixes renderString call. * Removes unnecessary styling. * Reverts mobile nav change. * Nuxt 3 Lazy Loading Search (#1022) * Uses lazyFetch for results. onSearchChange refreshes. Adds loading circle. * Removes console.log * Preserves old page when paging. * Diagnosing filtering bugs. * Fix single facet filtering * Implements useAuth in settings/account. * tiny ssr fix * Updating nuxt.config checklist. * Implements useAuth in revenue, moneitzation, and dashboard index pages. * Fixes setups. * Eliminates results when path changes. Adds animated logo. * Ensures loading animation renders on search page. --------- Co-authored-by: Jai A <jaiagr+gpg@pm.me> * Fix navigation issues * Square button fix (#1023) * Removes checklist from nuxt.config. * Modifies Nuxt CI to build after linting. * Fixes prettierignore file. * bug fixes * Update whitelist domains * Page improvements, fix CLS * Fix a lot of things * Fix project type redirect * Fix 404 errors * Fix user settings + hydration error * Final fixes * fix(creator-section): border radius on icons not aligning with bg (#1027) Co-authored-by: MagnusHJensen <magnus.holm.jensen@lego.dk> * Improvements to the mobile navbar (#984) * Transition between animation states on TextLogo * Remove unused refs * Fixes from review * Improvements to the mobile nav menu * fix avatar alt text * Nevermind, got confused for a moment * Tab bar, menu layout improvements * Highlight search icon when menu is open * Update layouts/default.vue Co-authored-by: Magnus Jensen <magnushjensen.mail@gmail.com> * Fix some issues * Use caret instead * Run prettier * Add create a project --------- Co-authored-by: Magnus Jensen <magnushjensen.mail@gmail.com> Co-authored-by: Geometrically <18202329+Geometrically@users.noreply.github.com> Co-authored-by: Jai A <jaiagr+gpg@pm.me> * Fix mobile menu issues * More issues * Fix lint --------- Co-authored-by: Kaeden Murphy <kmurphy@kaedenmurphy.dev> Co-authored-by: triphora <emmaffle@modrinth.com> Co-authored-by: Zach Baird <30800863+ZachBaird@users.noreply.github.com> Co-authored-by: stairman06 <36215135+stairman06@users.noreply.github.com> Co-authored-by: Zachary Baird <zdb1994@yahoo.com> Co-authored-by: Magnus Jensen <magnushjensen.mail@gmail.com> Co-authored-by: MagnusHJensen <magnus.holm.jensen@lego.dk>
This commit is contained in:
63
composables/auth.js
Normal file
63
composables/auth.js
Normal file
@@ -0,0 +1,63 @@
|
||||
export const useAuth = async (oldToken = null) => {
|
||||
const auth = useState('auth', () => ({
|
||||
user: null,
|
||||
token: '',
|
||||
headers: {},
|
||||
}))
|
||||
|
||||
if (!auth.value.user || oldToken) {
|
||||
auth.value = await initAuth(oldToken)
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
||||
|
||||
export const initAuth = async (oldToken = null) => {
|
||||
const auth = {
|
||||
user: null,
|
||||
token: '',
|
||||
headers: {},
|
||||
}
|
||||
const route = useRoute()
|
||||
const authCookie = useCookie('auth-token', {
|
||||
secure: true,
|
||||
sameSite: 'Strict',
|
||||
httpOnly: false,
|
||||
expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
||||
path: '/',
|
||||
})
|
||||
|
||||
if (oldToken) {
|
||||
authCookie.value = oldToken
|
||||
}
|
||||
|
||||
if (route.query.code) {
|
||||
authCookie.value = route.query.code
|
||||
}
|
||||
|
||||
if (authCookie.value) {
|
||||
auth.token = authCookie.value
|
||||
try {
|
||||
auth.user = await useBaseFetch('user', {
|
||||
headers: {
|
||||
Authorization: auth.token,
|
||||
},
|
||||
})
|
||||
} catch {}
|
||||
|
||||
auth.headers = {
|
||||
headers: {
|
||||
Authorization: auth.token,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return auth
|
||||
}
|
||||
|
||||
export const getAuthUrl = () => {
|
||||
const config = useRuntimeConfig()
|
||||
const route = useRoute()
|
||||
|
||||
return `${config.public.apiBaseUrl}auth/init?url=${config.public.siteUrl}${route.fullPath}`
|
||||
}
|
||||
46
composables/cosmetics.js
Normal file
46
composables/cosmetics.js
Normal file
@@ -0,0 +1,46 @@
|
||||
export const useCosmetics = () =>
|
||||
useState('cosmetics', () => {
|
||||
const cosmetics = useCookie('cosmetics', {
|
||||
maxAge: 60 * 60 * 24 * 365 * 10,
|
||||
sameSite: 'Strict',
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
if (!cosmetics.value) {
|
||||
cosmetics.value = {
|
||||
searchLayout: false,
|
||||
projectLayout: false,
|
||||
modpacksAlphaNotice: true,
|
||||
advancedRendering: true,
|
||||
externalLinksNewTab: true,
|
||||
notUsingBlockers: false,
|
||||
searchDisplayMode: {
|
||||
mod: 'list',
|
||||
plugin: 'list',
|
||||
resourcepack: 'gallery',
|
||||
modpack: 'list',
|
||||
shader: 'gallery',
|
||||
datapack: 'list',
|
||||
user: 'list',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return cosmetics.value
|
||||
})
|
||||
|
||||
export const saveCosmetics = () => {
|
||||
const cosmetics = useCosmetics()
|
||||
|
||||
const cosmeticsCookie = useCookie('cosmetics', {
|
||||
maxAge: 60 * 60 * 24 * 365 * 10,
|
||||
sameSite: 'Strict',
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
cosmeticsCookie.value = cosmetics.value
|
||||
}
|
||||
18
composables/date.js
Normal file
18
composables/date.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
|
||||
// eslint-disable-next-line import/no-named-as-default-member
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
export const useCurrentDate = () => useState('currentDate', () => Date.now())
|
||||
|
||||
export const updateCurrentDate = () => {
|
||||
const currentDate = useCurrentDate()
|
||||
|
||||
currentDate.value = Date.now()
|
||||
}
|
||||
|
||||
export const fromNow = (date) => {
|
||||
const currentDate = useCurrentDate()
|
||||
return dayjs(date).from(currentDate.value)
|
||||
}
|
||||
14
composables/fetch.js
Normal file
14
composables/fetch.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export const useBaseFetch = async (url, options = {}) => {
|
||||
const config = useRuntimeConfig()
|
||||
const base = process.server ? config.apiBaseUrl : config.public.apiBaseUrl
|
||||
|
||||
if (options.headers && process.server) {
|
||||
options.headers['x-ratelimit-key'] = config.rateLimitKey
|
||||
} else if (process.server) {
|
||||
options.headers = {
|
||||
'x-ratelimit-key': config.rateLimitKey,
|
||||
}
|
||||
}
|
||||
|
||||
return await $fetch(`${base}${url}`, options)
|
||||
}
|
||||
13
composables/loading.js
Normal file
13
composables/loading.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export const useLoading = () => useState('loading', () => false)
|
||||
|
||||
export const startLoading = () => {
|
||||
const loading = useLoading()
|
||||
|
||||
loading.value = true
|
||||
}
|
||||
|
||||
export const stopLoading = () => {
|
||||
const loading = useLoading()
|
||||
|
||||
loading.value = false
|
||||
}
|
||||
34
composables/notifs.js
Normal file
34
composables/notifs.js
Normal file
@@ -0,0 +1,34 @@
|
||||
export const useNotifications = () => useState('notifications', () => [])
|
||||
|
||||
export const addNotification = (notification) => {
|
||||
const notifications = useNotifications()
|
||||
|
||||
const existingNotif = notifications.value.find(
|
||||
(x) =>
|
||||
x.text === notification.text && x.title === notification.title && x.type === notification.type
|
||||
)
|
||||
if (existingNotif) {
|
||||
setNotificationTimer(existingNotif)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
notification.id = new Date()
|
||||
|
||||
setNotificationTimer(notification)
|
||||
notifications.value.push(notification)
|
||||
}
|
||||
|
||||
export const setNotificationTimer = (notification) => {
|
||||
if (!notification) return
|
||||
|
||||
const notifications = useNotifications()
|
||||
|
||||
if (notification.timer) {
|
||||
clearTimeout(notification.timer)
|
||||
}
|
||||
|
||||
notification.timer = setTimeout(() => {
|
||||
notifications.value.splice(notifications.value.indexOf(notification), 1)
|
||||
}, 30000)
|
||||
}
|
||||
62
composables/tag.js
Normal file
62
composables/tag.js
Normal file
@@ -0,0 +1,62 @@
|
||||
import tags from '~/generated/state.json'
|
||||
|
||||
export const useTags = () =>
|
||||
useState('tags', () => ({
|
||||
categories: tags.categories,
|
||||
loaders: tags.loaders,
|
||||
gameVersions: tags.gameVersions,
|
||||
donationPlatforms: tags.donationPlatforms,
|
||||
reportTypes: tags.reportTypes,
|
||||
projectTypes: [
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'mod',
|
||||
display: 'mod',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'plugin',
|
||||
display: 'plugin',
|
||||
},
|
||||
{
|
||||
actual: 'mod',
|
||||
id: 'datapack',
|
||||
display: 'data pack',
|
||||
},
|
||||
{
|
||||
actual: 'shader',
|
||||
id: 'shader',
|
||||
display: 'shader',
|
||||
},
|
||||
{
|
||||
actual: 'resourcepack',
|
||||
id: 'resourcepack',
|
||||
display: 'resource pack',
|
||||
},
|
||||
{
|
||||
actual: 'modpack',
|
||||
id: 'modpack',
|
||||
display: 'modpack',
|
||||
},
|
||||
],
|
||||
loaderData: {
|
||||
pluginLoaders: ['bukkit', 'spigot', 'paper', 'purpur', 'sponge'],
|
||||
pluginPlatformLoaders: ['bungeecord', 'waterfall', 'velocity'],
|
||||
allPluginLoaders: [
|
||||
'bukkit',
|
||||
'spigot',
|
||||
'paper',
|
||||
'purpur',
|
||||
'sponge',
|
||||
'bungeecord',
|
||||
'waterfall',
|
||||
'velocity',
|
||||
],
|
||||
dataPackLoaders: ['datapack'],
|
||||
modLoaders: ['forge', 'fabric', 'quilt', 'liteloader', 'modloader', 'rift'],
|
||||
},
|
||||
projectViewModes: ['list', 'grid', 'gallery'],
|
||||
approvedStatuses: ['approved', 'archived', 'unlisted', 'private'],
|
||||
rejectedStatuses: ['rejected', 'withheld'],
|
||||
staffRoles: ['moderator', 'admin'],
|
||||
}))
|
||||
55
composables/theme.js
Normal file
55
composables/theme.js
Normal file
@@ -0,0 +1,55 @@
|
||||
export const useTheme = () =>
|
||||
useState('theme', () => {
|
||||
const colorMode = useCookie('color-mode', {
|
||||
maxAge: 60 * 60 * 24 * 365 * 10,
|
||||
sameSite: 'Strict',
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
if (!colorMode.value) {
|
||||
colorMode.value = {
|
||||
value: 'dark',
|
||||
preference: 'system',
|
||||
}
|
||||
}
|
||||
|
||||
if (colorMode.value.preference !== 'system') {
|
||||
colorMode.value.value = colorMode.value.preference
|
||||
}
|
||||
|
||||
return colorMode.value
|
||||
})
|
||||
|
||||
export const updateTheme = (value, updatePreference = false) => {
|
||||
const theme = useTheme()
|
||||
|
||||
const themeCookie = useCookie('color-mode', {
|
||||
maxAge: 60 * 60 * 24 * 365 * 10,
|
||||
sameSite: 'Strict',
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
path: '/',
|
||||
})
|
||||
|
||||
if (value === 'system') {
|
||||
theme.value.preference = 'system'
|
||||
|
||||
const colorSchemeQueryList = window.matchMedia('(prefers-color-scheme: light)')
|
||||
if (colorSchemeQueryList.matches) {
|
||||
theme.value.value = 'light'
|
||||
} else {
|
||||
theme.value.value = 'dark'
|
||||
}
|
||||
} else {
|
||||
theme.value.value = value
|
||||
if (updatePreference) theme.value.preference = value
|
||||
}
|
||||
|
||||
if (process.client) {
|
||||
document.documentElement.className = `${theme.value.value}-mode`
|
||||
}
|
||||
|
||||
themeCookie.value = theme.value
|
||||
}
|
||||
112
composables/user.js
Normal file
112
composables/user.js
Normal file
@@ -0,0 +1,112 @@
|
||||
export const useUser = async (force = false) => {
|
||||
const user = useState('user', () => {})
|
||||
|
||||
if (!user.value || force || (user.value && Date.now() - user.value.lastUpdated > 300000)) {
|
||||
user.value = await initUser()
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
export const initUser = async () => {
|
||||
const auth = (await useAuth()).value
|
||||
|
||||
const user = {
|
||||
notifications: [],
|
||||
follows: [],
|
||||
projects: [],
|
||||
lastUpdated: 0,
|
||||
}
|
||||
|
||||
if (auth.user && auth.user.id) {
|
||||
try {
|
||||
const [notifications, follows, projects] = await Promise.all([
|
||||
useBaseFetch(`user/${auth.user.id}/notifications`, auth.headers),
|
||||
useBaseFetch(`user/${auth.user.id}/follows`, auth.headers),
|
||||
useBaseFetch(`user/${auth.user.id}/projects`, auth.headers),
|
||||
])
|
||||
|
||||
user.notifications = notifications
|
||||
user.follows = follows
|
||||
user.projects = projects
|
||||
user.lastUpdated = Date.now()
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
export const initUserNotifs = async () => {
|
||||
const auth = (await useAuth()).value
|
||||
const user = (await useUser()).value
|
||||
|
||||
if (auth.user && auth.user.id) {
|
||||
try {
|
||||
user.notifications = await useBaseFetch(`user/${auth.user.id}/notifications`, auth.headers)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const initUserFollows = async () => {
|
||||
const auth = (await useAuth()).value
|
||||
const user = (await useUser()).value
|
||||
|
||||
if (auth.user && auth.user.id) {
|
||||
try {
|
||||
user.follows = await useBaseFetch(`user/${auth.user.id}/follows`, auth.headers)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const initUserProjects = async () => {
|
||||
const auth = (await useAuth()).value
|
||||
const user = (await useUser()).value
|
||||
|
||||
if (auth.user && auth.user.id) {
|
||||
try {
|
||||
user.projects = await useBaseFetch(`user/${auth.user.id}/projects`, auth.headers)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const userFollowProject = async (project) => {
|
||||
const auth = (await useAuth()).value
|
||||
const user = (await useUser()).value
|
||||
|
||||
user.follows = user.follows.concat(project)
|
||||
|
||||
setTimeout(() => {
|
||||
useBaseFetch(`project/${project.id}/follow`, {
|
||||
method: 'POST',
|
||||
...auth.headers,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const userUnfollowProject = async (project) => {
|
||||
const auth = (await useAuth()).value
|
||||
const user = (await useUser()).value
|
||||
|
||||
user.follows = user.follows.filter((x) => x.id !== project.id)
|
||||
|
||||
setTimeout(() => {
|
||||
useBaseFetch(`project/${project.id}/follow`, {
|
||||
method: 'DELETE',
|
||||
...auth.headers,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const userDeleteNotification = async (id) => {
|
||||
const user = (await useUser()).value
|
||||
|
||||
user.notifications = user.notifications.filter((x) => x.id !== id)
|
||||
}
|
||||
Reference in New Issue
Block a user