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:
Geometrically
2023-03-09 10:05:32 -07:00
committed by GitHub
parent 5638f0f24b
commit 740357d120
145 changed files with 12371 additions and 37478 deletions

63
composables/auth.js Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}