mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-08-03 11:07:00 -04:00
(nuxt) dynamically load calendar events
This commit is contained in:
parent
a9abe47aa7
commit
6dcb92bdd8
@ -1,3 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { Day } from '../src/calendar/helpers.ts';
|
||||
import { loadCalendar } from '../src/data.ts';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
day?: Day;
|
||||
link?: boolean;
|
||||
}>(), {
|
||||
day: () => Day.today(),
|
||||
});
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const calendar = await loadCalendar();
|
||||
const events = calendar.getCurrentYear()!.eventsByDate[props.day.toString()];
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section
|
||||
v-if="config.calendar && config.calendar.enabled && events !== undefined"
|
||||
@ -23,26 +41,3 @@
|
||||
</nuxt-link>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { buildCalendar } from '../src/calendar/calendar.ts';
|
||||
import { Day } from '../src/calendar/helpers.ts';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
day: { default: () => Day.today(), type: Day },
|
||||
link: { type: Boolean },
|
||||
},
|
||||
setup(props) {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
return {
|
||||
config: useConfig(),
|
||||
events: buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear()!.eventsByDate[props.day.toString()],
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
@ -25,8 +25,8 @@ import { storeToRefs } from 'pinia';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
import logoSvg from '../public/logo/logo.svg?raw';
|
||||
import { buildCalendar } from '../src/calendar/calendar.ts';
|
||||
import { Day } from '../src/calendar/helpers.ts';
|
||||
import { loadCalendar } from '../src/data.ts';
|
||||
import { ImmutableArray } from '../src/helpers.ts';
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
@ -47,8 +47,7 @@ export default defineComponent({
|
||||
day: { default: () => Day.today(), type: Day },
|
||||
},
|
||||
async setup() {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
const calendarPromise = loadCalendar();
|
||||
const { data: stats } = await useFetch<{ overall: { users: number } }>(
|
||||
'/api/admin/stats-public',
|
||||
{ lazy: true },
|
||||
@ -57,7 +56,7 @@ export default defineComponent({
|
||||
return {
|
||||
stats,
|
||||
selectedDay: storeToRefs(useMainStore()).selectedDay,
|
||||
calendar: buildCalendar(runtimeConfig.public.baseUrl),
|
||||
calendar: await calendarPromise,
|
||||
};
|
||||
},
|
||||
data(): Data {
|
||||
|
@ -4,9 +4,8 @@ import { useNuxtApp } from 'nuxt/app';
|
||||
import useConfig from '~/composables/useConfig.ts';
|
||||
import useMainPronoun from '~/composables/useMainPronoun.ts';
|
||||
import useSpelling from '~/composables/useSpelling.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import type { TermsEntryRaw } from '~/src/classes.ts';
|
||||
import { loadPronounLibrary } from '~/src/data.ts';
|
||||
import { loadCalendar, loadPronounLibrary } from '~/src/data.ts';
|
||||
import { buildFlags } from '~/src/flags.ts';
|
||||
import { buildImageUrl } from '~/src/helpers.ts';
|
||||
import type { Profile, UserWithProfiles } from '~/src/profile.ts';
|
||||
@ -24,7 +23,6 @@ const props = withDefaults(defineProps<{
|
||||
const { $translator: translator } = useNuxtApp();
|
||||
const { convertName } = useSpelling();
|
||||
const config = useConfig();
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
const pronounLibrary = await loadPronounLibrary(config);
|
||||
|
||||
@ -40,7 +38,7 @@ const normaliseUrl = (url: string): string | null => {
|
||||
|
||||
const allFlags = buildFlags(config.locale);
|
||||
const minAge = config.ageLimit || 13;
|
||||
const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear();
|
||||
const year = (await loadCalendar()).getCurrentYear();
|
||||
|
||||
const countFlags = computed(() => {
|
||||
return (props.profile.flags?.length ?? 0) + (props.profile.customFlags?.length ?? 0);
|
||||
|
@ -3,9 +3,9 @@ import type { ComponentExposed } from 'vue-component-type-helpers';
|
||||
|
||||
import type Table from '~/components/Table.vue';
|
||||
import type TermsSubmitForm from '~/components/TermsSubmitForm.vue';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import { TermsEntry } from '~/src/classes.ts';
|
||||
import type { TermsEntryRaw } from '~/src/classes.ts';
|
||||
import { loadCalendar } from '~/src/data.ts';
|
||||
import { buildDict, clearLinkedText } from '~/src/helpers.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
@ -13,7 +13,6 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const { $translator: translator } = useNuxtApp();
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const config = useConfig();
|
||||
const filter = useFilterWithCategory();
|
||||
|
||||
@ -108,7 +107,7 @@ const remove = async (entry: TermsEntry): Promise<void> => {
|
||||
delete entries.value[entry.id];
|
||||
};
|
||||
|
||||
const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear()!;
|
||||
const year = (await loadCalendar()).getCurrentYear()!;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -240,9 +240,18 @@ export default defineNuxtConfig({
|
||||
}
|
||||
},
|
||||
async 'nitro:config'(nitroConfig) {
|
||||
if (nitroConfig.publicAssets === undefined) {
|
||||
nitroConfig.publicAssets = [];
|
||||
}
|
||||
nitroConfig.virtual ??= {};
|
||||
// workaround to have a dynamic import with variables which currently does not work in Rollup
|
||||
// https://github.com/rollup/rollup/issues/2097
|
||||
nitroConfig.virtual['#virtual/calendar/events.ts'] = () => {
|
||||
const imports = localeDescriptions.map((locale) => {
|
||||
return `${locale.code}: async () =>
|
||||
(await import('~/locale/${locale.code}/calendar/events.ts')).default`;
|
||||
});
|
||||
return `export default {${imports.join(', ')}};`;
|
||||
};
|
||||
|
||||
nitroConfig.publicAssets ??= [];
|
||||
nitroConfig.publicAssets.push(...(await Promise.all(codes.map(async (code) => [{
|
||||
baseURL: `/docs/${code}`,
|
||||
dir: await resolvePath(`~/locale/${code}/docs`),
|
||||
|
@ -4,8 +4,8 @@ import { useNuxtApp, useRoute } from 'nuxt/app';
|
||||
import Page from '~/components/Page.vue';
|
||||
import useConfig from '~/composables/useConfig.ts';
|
||||
import useSimpleHead from '~/composables/useSimpleHead.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import { Day } from '~/src/calendar/helpers.ts';
|
||||
import { loadCalendar } from '~/src/data.ts';
|
||||
|
||||
definePageMeta({
|
||||
name: 'calendar',
|
||||
@ -19,11 +19,10 @@ definePageMeta({
|
||||
});
|
||||
|
||||
const { $translator: translator } = useNuxtApp();
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
const config = useConfig();
|
||||
|
||||
const calendar = buildCalendar(runtimeConfig.public.baseUrl);
|
||||
const calendar = await loadCalendar();
|
||||
const year = route.params.year
|
||||
? calendar.getYear(parseInt(route.params.year as string))
|
||||
: calendar.getCurrentYear();
|
||||
|
@ -4,8 +4,8 @@ import { useNuxtApp, useRoute } from 'nuxt/app';
|
||||
import Page from '~/components/Page.vue';
|
||||
import useConfig from '~/composables/useConfig.ts';
|
||||
import useSimpleHead from '~/composables/useSimpleHead.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import { Day } from '~/src/calendar/helpers.ts';
|
||||
import { loadCalendar } from '~/src/data.ts';
|
||||
|
||||
definePageMeta({
|
||||
name: 'calendarDay',
|
||||
@ -19,7 +19,6 @@ definePageMeta({
|
||||
});
|
||||
|
||||
const { $translator: translator } = useNuxtApp();
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const route = useRoute();
|
||||
|
||||
const day = new Day(
|
||||
@ -34,7 +33,7 @@ useSimpleHead({
|
||||
}, translator);
|
||||
|
||||
const config = useConfig();
|
||||
const year = buildCalendar(runtimeConfig.public.baseUrl).getYear(day.year);
|
||||
const year = (await loadCalendar()).getYear(day.year);
|
||||
|
||||
const basic = route.query.layout === 'basic';
|
||||
</script>
|
||||
|
@ -11,8 +11,7 @@ import useSimpleHead from '~/composables/useSimpleHead.ts';
|
||||
import type { Config } from '~/locale/config.ts';
|
||||
import { birthdateRange, formatDate, parseDate } from '~/src/birthdate.ts';
|
||||
import { buildPronounUsage } from '~/src/buildPronoun.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import { loadPronounLibrary } from '~/src/data.ts';
|
||||
import { loadCalendar, loadPronounLibrary } from '~/src/data.ts';
|
||||
import { buildList, isValidLink } from '~/src/helpers.ts';
|
||||
import { addPronounInjectionKey } from '~/src/injectionKeys.ts';
|
||||
import opinions from '~/src/opinions.ts';
|
||||
@ -224,7 +223,7 @@ const visibilityIcons = {
|
||||
} as Record<ProfileVisibility, string[]>;
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const year = buildCalendar(runtimeConfig.public.baseUrl).getCurrentYear();
|
||||
const year = (await loadCalendar()).getCurrentYear();
|
||||
|
||||
const { mainPronoun } = useMainPronoun(pronounLibrary, formData, translator);
|
||||
|
||||
|
@ -7,7 +7,7 @@ import type { MatchInfo, SearchResult, AsPlainObject, Options } from 'minisearch
|
||||
|
||||
import type { Config } from '~/locale/config.ts';
|
||||
import { getPosts } from '~/server/blog.ts';
|
||||
import { pronounLibrary, pronouns } from '~/server/data.ts';
|
||||
import { loadCalendar, pronounLibrary, pronouns } from '~/server/data.ts';
|
||||
import { getInclusiveEntries } from '~/server/express/inclusive.ts';
|
||||
import { getNounEntries } from '~/server/express/nouns.ts';
|
||||
import { getSourcesEntries } from '~/server/express/sources.ts';
|
||||
@ -15,7 +15,6 @@ import { getTermsEntries } from '~/server/express/terms.ts';
|
||||
import { loadSuml, loadSumlFromBase } from '~/server/loader.ts';
|
||||
import { rootDir } from '~/server/paths.ts';
|
||||
import { shortForVariant } from '~/src/buildPronoun.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import { Day } from '~/src/calendar/helpers.ts';
|
||||
import forbidden from '~/src/forbidden.ts';
|
||||
import { clearLinkedText, buildImageUrl } from '~/src/helpers.ts';
|
||||
@ -810,9 +809,7 @@ const kinds: SearchKind[] = [
|
||||
}
|
||||
const base = config.calendar?.route;
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
|
||||
const calendar = buildCalendar(runtimeConfig.public.baseUrl);
|
||||
const calendar = await loadCalendar();
|
||||
const year = Day.today().year;
|
||||
|
||||
return (calendar.getYear(year)?.events ?? [])
|
||||
|
@ -5,11 +5,11 @@ import fs from 'fs';
|
||||
import Pageres from 'pageres';
|
||||
|
||||
import type { Config } from '../locale/config.ts';
|
||||
import { buildCalendar } from '../src/calendar/calendar.ts';
|
||||
|
||||
import dbConnection from './db.ts';
|
||||
|
||||
import { loadSuml } from '~/server/loader.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
|
||||
const __dirname = new URL('.', import.meta.url).pathname;
|
||||
|
||||
@ -60,7 +60,8 @@ const dumpNameDays = async (): Promise<void> => {
|
||||
const prevPath = `${__dirname}/../cache/calendar.json`;
|
||||
const prev = fs.existsSync(prevPath) ? JSON.parse(fs.readFileSync(prevPath, 'utf-8')) : {};
|
||||
|
||||
const current = buildCalendar(process.env.BASE_URL!).buildSummary();
|
||||
const localEvents = (await import(`../locale/${config.locale}/calendar/events.ts`)).default;
|
||||
const current = buildCalendar(localEvents, process.env.BASE_URL!).buildSummary();
|
||||
const changedYears = new Set();
|
||||
for (const day in current) {
|
||||
if (!Object.hasOwn(current, day)) {
|
||||
|
@ -1,6 +1,9 @@
|
||||
import localeEventImports from '#virtual/calendar/events.ts';
|
||||
import type { PronounExamplesData } from '~/locale/data.ts';
|
||||
import { rootDir } from '~/server/paths.ts';
|
||||
import { parsePronounGroups, parsePronouns } from '~/src/buildPronoun.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
import type { Calendar } from '~/src/calendar/helpers.ts';
|
||||
import { PronounLibrary } from '~/src/classes.ts';
|
||||
import { loadTsv } from '~/src/tsv.ts';
|
||||
|
||||
@ -10,3 +13,14 @@ const pronounGroups = parsePronounGroups(loadTsv(`${dataDir}/pronouns/pronounGro
|
||||
export const pronounLibrary = new PronounLibrary(global.config, pronounGroups, pronouns);
|
||||
|
||||
export const pronounExamples = loadTsv(`${dataDir}/pronouns/examples.tsv`) as PronounExamplesData[];
|
||||
|
||||
let calendar: Calendar | null = null;
|
||||
|
||||
export const loadCalendar = async () => {
|
||||
if (calendar === null) {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const localEvents = await localeEventImports[config.locale]();
|
||||
calendar = buildCalendar(localEvents, runtimeConfig.public.baseUrl);
|
||||
}
|
||||
return calendar;
|
||||
};
|
||||
|
@ -6,7 +6,6 @@ import SQL from 'sql-template-strings';
|
||||
|
||||
import type { LocaleDescription } from '../../locale/locales.ts';
|
||||
import type { Translations } from '../../locale/translations.ts';
|
||||
import { buildCalendar } from '../../src/calendar/calendar.ts';
|
||||
import { Calendar, Day } from '../../src/calendar/helpers.ts';
|
||||
import type { Event } from '../../src/calendar/helpers.ts';
|
||||
import { handleErrorAsync, clearLinkedText } from '../../src/helpers.ts';
|
||||
@ -15,13 +14,13 @@ import { loadSuml, loadSumlFromBase } from '../loader.ts';
|
||||
|
||||
import { normalise } from './user.ts';
|
||||
|
||||
import { loadCalendar } from '~/server/data.ts';
|
||||
|
||||
// TODO caching? // import { caches } from "../../src/cache";
|
||||
|
||||
const translations = loadSuml('translations') as Translations;
|
||||
const fallbackTranslations = loadSumlFromBase('locale/_base/translations') as Translations;
|
||||
|
||||
const calendar = buildCalendar(process.env.BASE_URL!);
|
||||
|
||||
const renderEvents = (yearEvents: Record<number, Event[]>, res: Response, onlyFirstDays: boolean = false, calNameExtra: string = '') => {
|
||||
const events = [];
|
||||
let i = 1;
|
||||
@ -79,7 +78,8 @@ const buildMessage = (events: string[], locale: LocaleDescription, day: Day, lin
|
||||
return message;
|
||||
};
|
||||
|
||||
const eventsSummary = (day: Day, locale: LocaleDescription) => {
|
||||
const eventsSummary = async (day: Day, locale: LocaleDescription) => {
|
||||
const calendar = await loadCalendar();
|
||||
const eventsRaw = calendar.getCurrentYear()!.eventsByDate[day.toString()];
|
||||
|
||||
const link = `${locale.url}/${encodeURIComponent(config.calendar!.route!)}/${day}`;
|
||||
@ -126,14 +126,14 @@ interface ProfileRaw {
|
||||
const router = Router();
|
||||
|
||||
router.get('/calendar/today', handleErrorAsync(async (req, res) => {
|
||||
return res.json(eventsSummary(
|
||||
return res.json(await eventsSummary(
|
||||
Day.today(),
|
||||
req.locales[config.locale],
|
||||
));
|
||||
}));
|
||||
|
||||
router.get('/calendar/:year-:month-:day', handleErrorAsync(async (req, res) => {
|
||||
return res.json(eventsSummary(
|
||||
return res.json(await eventsSummary(
|
||||
new Day(parseInt(req.params.year), parseInt(req.params.month), parseInt(req.params.day)),
|
||||
req.locales[config.locale],
|
||||
));
|
||||
@ -142,6 +142,7 @@ router.get('/calendar/:year-:month-:day', handleErrorAsync(async (req, res) => {
|
||||
const routeBase = `/queer-calendar-${config.locale}`;
|
||||
|
||||
router.get(`${routeBase}.ics`, handleErrorAsync(async (req, res) => {
|
||||
const calendar = await loadCalendar();
|
||||
const events: Record<number, Event[]> = {};
|
||||
for (const year of calendar.getAllYears()) {
|
||||
events[year.year] = year.events;
|
||||
@ -151,6 +152,7 @@ router.get(`${routeBase}.ics`, handleErrorAsync(async (req, res) => {
|
||||
}));
|
||||
|
||||
router.get(generateURIEncodedPathAlternative(`${routeBase}-@:username.ics`), handleErrorAsync(async (req, res) => {
|
||||
const calendar = await loadCalendar();
|
||||
const profiles = await req.db.all<Pick<ProfileRaw, 'events' | 'customEvents'>>(SQL`
|
||||
SELECT events, customEvents FROM profiles p
|
||||
LEFT JOIN users u ON p.userId = u.id
|
||||
@ -176,6 +178,7 @@ router.get(generateURIEncodedPathAlternative(`${routeBase}-@:username.ics`), han
|
||||
}));
|
||||
|
||||
router.get(`${routeBase}-:year-:uuid.ics`, handleErrorAsync(async (req, res) => {
|
||||
const calendar = await loadCalendar();
|
||||
const year = calendar.getYear(parseInt(req.params.year));
|
||||
if (!year) {
|
||||
return res.status(404).json({ error: 'Not found' });
|
||||
@ -185,6 +188,7 @@ router.get(`${routeBase}-:year-:uuid.ics`, handleErrorAsync(async (req, res) =>
|
||||
}));
|
||||
|
||||
router.get(`${routeBase}-:year.ics`, handleErrorAsync(async (req, res) => {
|
||||
const calendar = await loadCalendar();
|
||||
const year = calendar.getYear(parseInt(req.params.year));
|
||||
if (!year) {
|
||||
return res.status(404).json({ error: 'Not found' });
|
||||
|
@ -1,18 +1,8 @@
|
||||
import localEvents from '../../data/calendar/events.ts';
|
||||
import rawNamedays from '../../data/names/namedays.json' with { type: 'json' };
|
||||
import internationalEvents from '../../locale/_/calendar/events.ts';
|
||||
import internationalEvents from '~/locale/_/calendar/events.ts';
|
||||
import { Calendar } from '~/src/calendar/helpers.ts';
|
||||
import type { Event } from '~/src/calendar/helpers.ts';
|
||||
|
||||
import { Calendar, Event, EventLevel, day } from './helpers.ts';
|
||||
|
||||
const _namedays = Object.entries(rawNamedays as Record<string, string[]>)
|
||||
.flatMap(([name, namedays]) => {
|
||||
return namedays.map((nameday) => {
|
||||
const [m, d] = nameday.split('-');
|
||||
return new Event(`nameday$${name}`, null, parseInt(m), day(parseInt(d)), EventLevel.Nameday);
|
||||
});
|
||||
});
|
||||
|
||||
export const buildCalendar = (baseUrl: string) => {
|
||||
export const buildCalendar = (localEvents: Event[], baseUrl: string) => {
|
||||
return new Calendar(
|
||||
[...internationalEvents, ...localEvents], // TODO , ...namedays
|
||||
baseUrl,
|
||||
|
15
src/data.ts
15
src/data.ts
@ -4,6 +4,7 @@ import { buildDict, buildList } from './helpers.ts';
|
||||
|
||||
import type { Config } from '~/locale/config.ts';
|
||||
import type { PronounExamplesData } from '~/locale/data.ts';
|
||||
import { buildCalendar } from '~/src/calendar/calendar.ts';
|
||||
|
||||
export const getLocale = () => {
|
||||
return useRuntimeConfig().public.locale;
|
||||
@ -97,3 +98,17 @@ export const loadNameCount = async () => {
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
export const loadCalendar = async () => {
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const localEvents = (await import(`~/locale/${getLocale()}/calendar/events.ts`)).default;
|
||||
/* const rawNamedays = (await import(`~/locale/${config.locale}/names/namedays.json`)).default;
|
||||
const namedays = Object.entries(rawNamedays as Record<string, string[]>)
|
||||
.flatMap(([name, namedays]) => {
|
||||
return namedays.map((nameday) => {
|
||||
const [m, d] = nameday.split('-');
|
||||
return new Event(`nameday$${name}`, null, parseInt(m), day(parseInt(d)), EventLevel.Nameday);
|
||||
});
|
||||
}); */
|
||||
return buildCalendar(localEvents, runtimeConfig.public.baseUrl);
|
||||
};
|
||||
|
6
types/virtual.d.ts
vendored
Normal file
6
types/virtual.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
declare module '#virtual/calendar/events.ts' {
|
||||
import type { Event } from '~/src/calendar/helpers.ts';
|
||||
|
||||
declare const localEventsByLocale: Record<string, () => Promise<Event[]>>;
|
||||
export default localEventsByLocale;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user