diff --git a/apps/frontend/COPYING.md b/apps/frontend/COPYING.md index ceea2a7808..9a54fca3a2 100644 --- a/apps/frontend/COPYING.md +++ b/apps/frontend/COPYING.md @@ -15,6 +15,13 @@ This includes, but may not be limited to, the following files: - src/components/brand/\* - src/public/favicon.ico - src/public/favicon-light.ico +- src/public/favicon-settings.ico +- src/public/favicon-light-settings.ico +- src/public/favicon-32x32.png +- src/public/favicon-light-32x32.png +- src/public/favicon-settings-32x32.png +- src/public/favicon-light-settings-32x32.png +- src/public/dev-favicons/\* - src/public/news/\* ## External logos diff --git a/apps/frontend/nuxt.config.ts b/apps/frontend/nuxt.config.ts index e15e5169dc..0620e4d36e 100644 --- a/apps/frontend/nuxt.config.ts +++ b/apps/frontend/nuxt.config.ts @@ -19,12 +19,6 @@ const preloadedFonts = [ 'inter/Inter-Bold.woff2', ] -const favicons = { - '(prefers-color-scheme:no-preference)': '/favicon-light.ico', - '(prefers-color-scheme:light)': '/favicon-light.ico', - '(prefers-color-scheme:dark)': '/favicon.ico', -} - const PROD_MODRINTH_URL = 'https://modrinth.com' const STAGING_MODRINTH_URL = 'https://staging.modrinth.com' @@ -50,12 +44,6 @@ export default defineNuxtConfig({ crossorigin: 'anonymous', } }), - ...Object.entries(favicons).map(([media, href]): object => { - return { rel: 'icon', type: 'image/x-icon', href, media } - }), - ...Object.entries(favicons).map(([media, href]): object => { - return { rel: 'apple-touch-icon', type: 'image/x-icon', href, media, sizes: '64x64' } - }), { rel: 'search', type: 'application/opensearchdescription+xml', diff --git a/apps/frontend/src/composables/favicon.ts b/apps/frontend/src/composables/favicon.ts new file mode 100644 index 0000000000..14522818ba --- /dev/null +++ b/apps/frontend/src/composables/favicon.ts @@ -0,0 +1,22 @@ +import { type MaybeRefOrGetter, toValue } from 'vue' + +import { + type FaviconVariant, + getFaviconHeadLinks, + resolveFaviconEnvironment, +} from '~/utils/favicon.ts' + +export function useFaviconEnvironment() { + const config = useRuntimeConfig() + const url = useRequestURL() + + return computed(() => resolveFaviconEnvironment(config, url.hostname)) +} + +export function useFavicon(variant: MaybeRefOrGetter = 'default') { + const environment = useFaviconEnvironment() + + useHead({ + link: () => getFaviconHeadLinks(toValue(variant), environment.value), + }) +} diff --git a/apps/frontend/src/composables/project-settings-head.ts b/apps/frontend/src/composables/project-settings-head.ts index 9c9c94ae87..6775ab6da8 100644 --- a/apps/frontend/src/composables/project-settings-head.ts +++ b/apps/frontend/src/composables/project-settings-head.ts @@ -1,26 +1,10 @@ -import { - defineMessages, - injectProjectPageContext, - type MessageDescriptor, - useVIntl, -} from '@modrinth/ui' - -const messages = defineMessages({ - headTitle: { - id: 'project.settings.head-title', - defaultMessage: '⚙️ {section} - {project}', - }, -}) +import { injectProjectPageContext, type MessageDescriptor, useVIntl } from '@modrinth/ui' export function useProjectSettingsHeadTitle(section: MessageDescriptor) { const { formatMessage } = useVIntl() const { projectV2: project } = injectProjectPageContext() useHead({ - title: () => - `${formatMessage(messages.headTitle, { - section: formatMessage(section), - project: project.value.title, - })}`, + title: () => `${formatMessage(section)} - ${project.value.title}`, }) } diff --git a/apps/frontend/src/layouts/default.vue b/apps/frontend/src/layouts/default.vue index fbf55a146d..4751a030b4 100644 --- a/apps/frontend/src/layouts/default.vue +++ b/apps/frontend/src/layouts/default.vue @@ -1179,6 +1179,7 @@ const messages = defineMessages({ }, }) +useFavicon() useHead({ link: [ { diff --git a/apps/frontend/src/locales/ar-SA/index.json b/apps/frontend/src/locales/ar-SA/index.json index 51a5afd8c6..47b30ad99f 100644 --- a/apps/frontend/src/locales/ar-SA/index.json +++ b/apps/frontend/src/locales/ar-SA/index.json @@ -3554,9 +3554,6 @@ "project.settings.general.url.title": { "message": "رابط" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.title": { "message": "تمويل" }, diff --git a/apps/frontend/src/locales/de-CH/index.json b/apps/frontend/src/locales/de-CH/index.json index 1753ef43e6..c87485e341 100644 --- a/apps/frontend/src/locales/de-CH/index.json +++ b/apps/frontend/src/locales/de-CH/index.json @@ -4028,9 +4028,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Projekte auf Modrinth nehmen automatisch am Belohnungsprogramm teil. Falls du mit diesem Projekt keine Einnahmen erzielen möchtest (oder aus rechtlichen Gründen nicht darfst), kannst du dies hier deaktivieren." }, diff --git a/apps/frontend/src/locales/de-DE/index.json b/apps/frontend/src/locales/de-DE/index.json index 9341e90d55..732779edb2 100644 --- a/apps/frontend/src/locales/de-DE/index.json +++ b/apps/frontend/src/locales/de-DE/index.json @@ -4028,9 +4028,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Projekte auf Modrinth nehmen automatisch am Belohnungsprogramm teil. Falls du mit diesem Projekt keine Einnahmen erzielen möchtest (oder aus rechtlichen Gründen nicht darfst), kannst du dies hier deaktivieren." }, diff --git a/apps/frontend/src/locales/en-US/index.json b/apps/frontend/src/locales/en-US/index.json index 6f3e4b030d..36b9560015 100644 --- a/apps/frontend/src/locales/en-US/index.json +++ b/apps/frontend/src/locales/en-US/index.json @@ -4037,9 +4037,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Projects on Modrinth are automatically enrolled in the Rewards Program. If you don't want to (or can't for legal reasons) earn revenue from this project, you can turn it off here." }, diff --git a/apps/frontend/src/locales/es-419/index.json b/apps/frontend/src/locales/es-419/index.json index dcf5f0ca5a..cc2d944a80 100644 --- a/apps/frontend/src/locales/es-419/index.json +++ b/apps/frontend/src/locales/es-419/index.json @@ -4028,9 +4028,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Los proyectos en Modrinth están inscritos automáticamente en el Rewards Program. Si usted no quiere (o no puede por razones legales) obtener ingresos de este proyecto, puede desactivarlo aquí." }, diff --git a/apps/frontend/src/locales/fr-FR/index.json b/apps/frontend/src/locales/fr-FR/index.json index a29d190fc3..8bc258d58a 100644 --- a/apps/frontend/src/locales/fr-FR/index.json +++ b/apps/frontend/src/locales/fr-FR/index.json @@ -3938,9 +3938,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.title": { "message": "Monétisation" }, diff --git a/apps/frontend/src/locales/it-IT/index.json b/apps/frontend/src/locales/it-IT/index.json index f97135d822..29d0ed2341 100644 --- a/apps/frontend/src/locales/it-IT/index.json +++ b/apps/frontend/src/locales/it-IT/index.json @@ -4016,9 +4016,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "I progetti su Modrinth vengono automaticamente iscritti al Programma Premi. Se non vuoi guadagnare da questo progetto (o non puoi per motivi legali), puoi disattivarlo qui." }, diff --git a/apps/frontend/src/locales/pl-PL/index.json b/apps/frontend/src/locales/pl-PL/index.json index 38633eeccc..99314c5c1d 100644 --- a/apps/frontend/src/locales/pl-PL/index.json +++ b/apps/frontend/src/locales/pl-PL/index.json @@ -3854,9 +3854,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.title": { "message": "Monetyzacja" }, diff --git a/apps/frontend/src/locales/pt-BR/index.json b/apps/frontend/src/locales/pt-BR/index.json index 72310d7b01..151f71a783 100644 --- a/apps/frontend/src/locales/pt-BR/index.json +++ b/apps/frontend/src/locales/pt-BR/index.json @@ -3962,9 +3962,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.permissions.attention-needed.description.proj-approved": { "message": "Forneça a prova que possui permissão para redistribuir todos os seguintes arquivos. Após a conclusão, as versões retidas serão publicadas automaticamente." }, diff --git a/apps/frontend/src/locales/ru-RU/index.json b/apps/frontend/src/locales/ru-RU/index.json index a7be871b79..d1acc7ad67 100644 --- a/apps/frontend/src/locales/ru-RU/index.json +++ b/apps/frontend/src/locales/ru-RU/index.json @@ -4019,9 +4019,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} • {project}" - }, "project.settings.monetization.description": { "message": "Проекты на Modrinth автоматически зачисляются в программу наград. Если вы не хотите (или не можете по юридическим причинам) получать доход от этого проекта, вы можете отключить его здесь." }, diff --git a/apps/frontend/src/locales/sv-SE/index.json b/apps/frontend/src/locales/sv-SE/index.json index e9b2802472..9e0d5a8ddb 100644 --- a/apps/frontend/src/locales/sv-SE/index.json +++ b/apps/frontend/src/locales/sv-SE/index.json @@ -3485,9 +3485,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} – {project}" - }, "project.settings.permissions.attention-needed.title": { "message": "Okänt externt innehåll" }, diff --git a/apps/frontend/src/locales/tr-TR/index.json b/apps/frontend/src/locales/tr-TR/index.json index 2d9040478f..bbfc56269d 100644 --- a/apps/frontend/src/locales/tr-TR/index.json +++ b/apps/frontend/src/locales/tr-TR/index.json @@ -3962,9 +3962,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Modrinth’teki projeler otomatik olarak Ödül Programı’na kaydedilir. Bu projeden gelir elde etmek istemiyorsanız (veya yasal nedenlerle edemiyorsanız), buradan kapatabilirsiniz." }, diff --git a/apps/frontend/src/locales/uk-UA/index.json b/apps/frontend/src/locales/uk-UA/index.json index f02910aa26..d61de69c23 100644 --- a/apps/frontend/src/locales/uk-UA/index.json +++ b/apps/frontend/src/locales/uk-UA/index.json @@ -4025,9 +4025,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Проєкти на Modrinth автоматично реєструються в Програмі винагород. Якщо ви не хочете (або не можете з юридичних причин) отримувати дохід від цього проєкту, ви можете вимкнути його тут." }, diff --git a/apps/frontend/src/locales/zh-CN/index.json b/apps/frontend/src/locales/zh-CN/index.json index 06c1c49d61..f9adaf04c5 100644 --- a/apps/frontend/src/locales/zh-CN/index.json +++ b/apps/frontend/src/locales/zh-CN/index.json @@ -4025,9 +4025,6 @@ "project.settings.general.url.title": { "message": "URL" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Modrints上的项目自动注册于 奖励方案如果你不想(或由于法律原因不能)从这个项目赚取收入,你可以在这里关闭它。" }, diff --git a/apps/frontend/src/locales/zh-TW/index.json b/apps/frontend/src/locales/zh-TW/index.json index a7cd397b14..fcabb641de 100644 --- a/apps/frontend/src/locales/zh-TW/index.json +++ b/apps/frontend/src/locales/zh-TW/index.json @@ -4025,9 +4025,6 @@ "project.settings.general.url.title": { "message": "網址" }, - "project.settings.head-title": { - "message": "⚙️ {section} - {project}" - }, "project.settings.monetization.description": { "message": "Modrinth 上的專案會自動加入獎勵計畫。如果你不想(或因法律原因無法)從專案獲取收益,可在這裡關閉營利功能。" }, diff --git a/apps/frontend/src/pages/[type]/[project].vue b/apps/frontend/src/pages/[type]/[project].vue index c95d9ae700..f601a5964a 100644 --- a/apps/frontend/src/pages/[type]/[project].vue +++ b/apps/frontend/src/pages/[type]/[project].vue @@ -1272,6 +1272,7 @@ const { data: thread } = useQuery({ }) const isSettings = computed(() => route.name.startsWith('type-project-settings')) +useFavicon(() => (isSettings.value ? 'settings' : 'default')) // Jank modpack loaders fix const versionsRaw = computed(() => { diff --git a/apps/frontend/src/pages/organization/[organization].vue b/apps/frontend/src/pages/organization/[organization].vue index c793e2ff72..00605dae96 100644 --- a/apps/frontend/src/pages/organization/[organization].vue +++ b/apps/frontend/src/pages/organization/[organization].vue @@ -233,6 +233,7 @@ if (route.path.includes('settings')) { // hacky way to show the edit button on the corner of the card. const routeHasSettings = computed(() => route.path.includes('settings')) +useFavicon(() => (routeHasSettings.value ? 'settings' : 'default')) const client = injectModrinthClient() const queryClient = useQueryClient() diff --git a/apps/frontend/src/pages/settings.vue b/apps/frontend/src/pages/settings.vue index 291c7c86d8..ef69d5f312 100644 --- a/apps/frontend/src/pages/settings.vue +++ b/apps/frontend/src/pages/settings.vue @@ -149,6 +149,7 @@ const route = useNativeRoute() const auth = await useAuth() const flags = useFeatureFlags() +useFavicon('settings') useSeoMeta({ robots: 'noindex', }) diff --git a/apps/frontend/src/public/dev-favicons/favicon-local-light-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-local-light-settings.svg new file mode 100644 index 0000000000..3740f677f0 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-local-light-settings.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-local-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-local-settings.svg new file mode 100644 index 0000000000..b2ec18ff28 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-local-settings.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-local.svg b/apps/frontend/src/public/dev-favicons/favicon-local.svg new file mode 100644 index 0000000000..e044d27690 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-local.svg @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-preview-light-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-preview-light-settings.svg new file mode 100644 index 0000000000..36e853d7d5 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-preview-light-settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-preview-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-preview-settings.svg new file mode 100644 index 0000000000..ee4977585a --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-preview-settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-preview.svg b/apps/frontend/src/public/dev-favicons/favicon-preview.svg new file mode 100644 index 0000000000..c8fdfd710f --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-preview.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-staging-light-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-staging-light-settings.svg new file mode 100644 index 0000000000..eaa109608f --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-staging-light-settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-staging-settings.svg b/apps/frontend/src/public/dev-favicons/favicon-staging-settings.svg new file mode 100644 index 0000000000..5cb62c5222 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-staging-settings.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/dev-favicons/favicon-staging.svg b/apps/frontend/src/public/dev-favicons/favicon-staging.svg new file mode 100644 index 0000000000..bb46548586 --- /dev/null +++ b/apps/frontend/src/public/dev-favicons/favicon-staging.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/frontend/src/public/favicon-32x32.png b/apps/frontend/src/public/favicon-32x32.png new file mode 100644 index 0000000000..f0e266ce05 Binary files /dev/null and b/apps/frontend/src/public/favicon-32x32.png differ diff --git a/apps/frontend/src/public/favicon-light-32x32.png b/apps/frontend/src/public/favicon-light-32x32.png new file mode 100644 index 0000000000..ef59c57e1e Binary files /dev/null and b/apps/frontend/src/public/favicon-light-32x32.png differ diff --git a/apps/frontend/src/public/favicon-light-settings-32x32.png b/apps/frontend/src/public/favicon-light-settings-32x32.png new file mode 100644 index 0000000000..e7d0495f32 Binary files /dev/null and b/apps/frontend/src/public/favicon-light-settings-32x32.png differ diff --git a/apps/frontend/src/public/favicon-light-settings.ico b/apps/frontend/src/public/favicon-light-settings.ico new file mode 100644 index 0000000000..798066a3ad Binary files /dev/null and b/apps/frontend/src/public/favicon-light-settings.ico differ diff --git a/apps/frontend/src/public/favicon-light.ico b/apps/frontend/src/public/favicon-light.ico index 3b4e67a278..1b62c15c21 100644 Binary files a/apps/frontend/src/public/favicon-light.ico and b/apps/frontend/src/public/favicon-light.ico differ diff --git a/apps/frontend/src/public/favicon-settings-32x32.png b/apps/frontend/src/public/favicon-settings-32x32.png new file mode 100644 index 0000000000..766e25dc67 Binary files /dev/null and b/apps/frontend/src/public/favicon-settings-32x32.png differ diff --git a/apps/frontend/src/public/favicon-settings.ico b/apps/frontend/src/public/favicon-settings.ico new file mode 100644 index 0000000000..1f0147d935 Binary files /dev/null and b/apps/frontend/src/public/favicon-settings.ico differ diff --git a/apps/frontend/src/public/favicon.ico b/apps/frontend/src/public/favicon.ico index c2ccbab905..3d3c867711 100644 Binary files a/apps/frontend/src/public/favicon.ico and b/apps/frontend/src/public/favicon.ico differ diff --git a/apps/frontend/src/utils/favicon.ts b/apps/frontend/src/utils/favicon.ts new file mode 100644 index 0000000000..75087601a3 --- /dev/null +++ b/apps/frontend/src/utils/favicon.ts @@ -0,0 +1,110 @@ +export type FaviconVariant = 'default' | 'settings' +export type FaviconEnvironment = 'local' | 'preview' | 'staging' + +const PRODUCTION_FAVICON_HREFS = { + default: { + light: '/favicon-light-32x32.png', + dark: '/favicon-32x32.png', + }, + settings: { + light: '/favicon-light-settings-32x32.png', + dark: '/favicon-settings-32x32.png', + }, +} as const + +const FAVICON_MEDIA = [ + '(prefers-color-scheme:no-preference)', + '(prefers-color-scheme:light)', + '(prefers-color-scheme:dark)', +] as const + +const STAGING_API_PREFIX = 'https://staging-api.modrinth.com' + +export function resolveFaviconEnvironment( + config: { + public: { + production?: boolean + preview?: boolean + apiBaseUrl?: string + } + }, + hostname: string, +): FaviconEnvironment | null { + if (!config.public.production || hostname === 'localhost' || hostname === '127.0.0.1') { + return 'local' + } + + if (String(config.public.apiBaseUrl).startsWith(STAGING_API_PREFIX)) { + return 'staging' + } + + if (config.public.preview) { + return 'preview' + } + + return null +} + +function getFaviconHrefs(variant: FaviconVariant, environment: FaviconEnvironment | null) { + if (!environment) { + return { + ...PRODUCTION_FAVICON_HREFS[variant], + type: 'image/png', + sizes: '32x32', + } + } + + if (variant === 'settings') { + return { + light: `/dev-favicons/favicon-${environment}-light-settings.svg`, + dark: `/dev-favicons/favicon-${environment}-settings.svg`, + type: 'image/svg+xml', + sizes: 'any', + } + } + + const href = `/dev-favicons/favicon-${environment}.svg` + return { + light: href, + dark: href, + type: 'image/svg+xml', + sizes: 'any', + } +} + +export function getFaviconHeadLinks( + variant: FaviconVariant = 'default', + environment: FaviconEnvironment | null = null, +) { + const icons = getFaviconHrefs(variant, environment) + const appleTouchIcons = getFaviconHrefs(variant, null) + const hrefByMedia = { + '(prefers-color-scheme:no-preference)': icons.light, + '(prefers-color-scheme:light)': icons.light, + '(prefers-color-scheme:dark)': icons.dark, + } + const appleHrefByMedia = { + '(prefers-color-scheme:no-preference)': appleTouchIcons.light, + '(prefers-color-scheme:light)': appleTouchIcons.light, + '(prefers-color-scheme:dark)': appleTouchIcons.dark, + } + + return FAVICON_MEDIA.flatMap((media) => [ + { + rel: 'icon', + type: icons.type, + sizes: icons.sizes, + href: hrefByMedia[media], + media, + key: `favicon-${media}`, + }, + { + rel: 'apple-touch-icon', + type: appleTouchIcons.type, + href: appleHrefByMedia[media], + media, + sizes: appleTouchIcons.sizes, + key: `apple-touch-icon-${media}`, + }, + ]) +}