mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-08 06:51:43 -04:00
(ts) migrate some components to composition API with typescript
This commit is contained in:
parent
d61f5c306b
commit
9516a93501
@ -219,7 +219,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import forbidden from '../src/forbidden.js';
|
||||
import forbidden from '../src/forbidden.ts';
|
||||
import { sleep } from '../src/helpers.ts';
|
||||
import useDialogue from '../composables/useDialogue.ts';
|
||||
|
||||
|
@ -1,5 +1,57 @@
|
||||
<script setup lang="ts">
|
||||
import Columnist from 'avris-columnist';
|
||||
import type { Post } from '~/server/express/blog.ts';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
|
||||
const props = defineProps<{
|
||||
posts: string[] | Post[];
|
||||
details?: boolean;
|
||||
}>();
|
||||
|
||||
const { data: postsFull } = useAsyncData(`posts-${JSON.stringify(props.posts)}`, async () => {
|
||||
if (!props.posts.length) {
|
||||
return [];
|
||||
}
|
||||
if (typeof props.posts[0] === 'object') {
|
||||
return props.posts;
|
||||
}
|
||||
return await $fetch('/api/blog', {
|
||||
params: {
|
||||
slugs: props.posts,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const shortcuts: Record<string, string | undefined> = {};
|
||||
if (config.blog && config.blog.shortcuts) {
|
||||
for (const shortcut in config.blog.shortcuts) {
|
||||
if (!config.blog.shortcuts.hasOwnProperty(shortcut)) {
|
||||
continue;
|
||||
}
|
||||
shortcuts[config.blog.shortcuts[shortcut]] = shortcut;
|
||||
}
|
||||
}
|
||||
|
||||
const generateLink = (slug: string): string => {
|
||||
const keepFullPath = config.blog?.keepFullPath || [];
|
||||
return shortcuts[slug] !== undefined && !keepFullPath.includes(slug)
|
||||
? `/${shortcuts[slug]}`
|
||||
: `/${config.links.blogRoute}/${slug}`;
|
||||
};
|
||||
|
||||
const entries = useTemplateRef<HTMLDivElement>('entries');
|
||||
onMounted(async () => {
|
||||
if (entries.value) {
|
||||
const columnist = new Columnist(entries.value);
|
||||
columnist.start();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="columnist-wall row">
|
||||
<div ref="entries" class="columnist-wall row">
|
||||
<div v-for="post in postsFull" class="columnist-column col-12 col-sm-6 col-md-4 mb-3">
|
||||
<div class="card shadow">
|
||||
<nuxt-link v-if="post.hero" :to="generateLink(post.slug)">
|
||||
@ -30,64 +82,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Columnist from 'avris-columnist';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
posts: { required: true },
|
||||
details: { type: Boolean },
|
||||
},
|
||||
setup(props) {
|
||||
const { data: postsFull } = useAsyncData(`posts-${JSON.stringify(props.posts)}`, async () => {
|
||||
if (!props.posts.length) {
|
||||
return [];
|
||||
}
|
||||
if (typeof props.posts[0] === 'object') {
|
||||
return props.posts;
|
||||
}
|
||||
return await $fetch('/api/blog', {
|
||||
params: {
|
||||
slugs: props.posts,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
config: useConfig(),
|
||||
postsFull,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
const shortcuts = {};
|
||||
if (this.config.blog && this.config.blog.shortcuts) {
|
||||
for (const shortcut in this.config.blog.shortcuts) {
|
||||
if (!this.config.blog.shortcuts.hasOwnProperty(shortcut)) {
|
||||
continue;
|
||||
}
|
||||
shortcuts[this.config.blog.shortcuts[shortcut]] = shortcut;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
shortcuts,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
const columnist = new Columnist(this.$el);
|
||||
columnist.start();
|
||||
},
|
||||
methods: {
|
||||
generateLink(slug) {
|
||||
return this.shortcuts[slug] !== undefined && !(this.config.blog.keepFullPath || []).includes(slug)
|
||||
? `/${this.shortcuts[slug]}`
|
||||
: `/${this.config.links.blogRoute}/${slug}`;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.columnist-wall > .columnist-column {
|
||||
transition: margin-top .2s ease-in-out;
|
||||
|
@ -1,3 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const store = useMainStore();
|
||||
const darkMode = store.darkMode;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Submenu
|
||||
v-if="config.community"
|
||||
@ -44,22 +53,3 @@
|
||||
}]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'pinia';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
return {
|
||||
config: useConfig(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useMainStore, [
|
||||
'darkMode',
|
||||
]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -185,14 +185,14 @@
|
||||
<!--<div class="container">
|
||||
<SafariWarning dismissable />
|
||||
</div>-->
|
||||
<div v-if="$user() && $user().bannedReason" class="alert alert-danger mb-0 container">
|
||||
<div v-if="$user() && $user()!.bannedReason" class="alert alert-danger mb-0 container">
|
||||
<p class="h4 mb-2">
|
||||
<Icon v="ban" />
|
||||
<T>ban.header</T>
|
||||
</p>
|
||||
<p>
|
||||
<T>ban.reason</T><T>quotation.colon</T>
|
||||
{{ $user().bannedReason }}
|
||||
{{ $user()!.bannedReason }}
|
||||
</p>
|
||||
<p>
|
||||
<T>ban.termsIntro</T><T>quotation.colon</T>
|
||||
@ -200,7 +200,7 @@
|
||||
<blockquote class="small">
|
||||
<T>terms.content.content.violations</T>
|
||||
<template v-for="(violation, i) in forbidden">
|
||||
<T :class="[$user().bannedTerms.includes(violation) ? 'fw-bold' : '']">terms.content.content.violationsExamples.{{ violation }}</T><template v-if="i !== forbidden.length - 1">
|
||||
<T :class="[$user()!.bannedTerms?.includes(violation) ? 'fw-bold' : '']">terms.content.content.violationsExamples.{{ violation }}</T><template v-if="i !== forbidden.length - 1">
|
||||
,
|
||||
</template>
|
||||
</template>.
|
||||
@ -218,19 +218,34 @@
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { mapState } from 'pinia';
|
||||
import { DateTime } from 'luxon';
|
||||
import forbidden from '../src/forbidden.js';
|
||||
import forbidden from '../src/forbidden.ts';
|
||||
import NounsNav from '../data/nouns/NounsNav.vue';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
import type { User } from '../src/user.ts';
|
||||
|
||||
export default {
|
||||
interface HeaderLink {
|
||||
header?: boolean;
|
||||
link: string;
|
||||
avatar?: User | null;
|
||||
icon: string;
|
||||
text: string;
|
||||
textLong?: string;
|
||||
extra?: (string | null)[];
|
||||
desktop?: boolean;
|
||||
mobile?: boolean;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { NounsNav },
|
||||
setup() {
|
||||
const hoverItem = ref<HeaderLink | null>(null);
|
||||
return {
|
||||
config: useConfig(),
|
||||
hoverItem,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
@ -239,7 +254,6 @@ export default {
|
||||
hamburgerShown: false,
|
||||
censusDismissed: false,
|
||||
forbidden,
|
||||
hoverItem: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -247,8 +261,8 @@ export default {
|
||||
'user',
|
||||
'darkMode',
|
||||
]),
|
||||
links() {
|
||||
const links = [];
|
||||
links(): HeaderLink[] {
|
||||
const links: HeaderLink[] = [];
|
||||
|
||||
links.push({
|
||||
header: true,
|
||||
@ -351,14 +365,14 @@ export default {
|
||||
this.config.contact && this.config.contact.team && this.config.contact.team.enabled
|
||||
) {
|
||||
const extra = [
|
||||
this.config.terminology && this.config.terminology.enabled && this.config.terminology.published ? `/${this.config.terminology.route}` : '',
|
||||
this.config.calendar && this.config.calendar.enabled ? `/${this.config.calendar.route}` : '',
|
||||
this.config.census && this.config.census.enabled ? `/${this.config.census.route}` : '',
|
||||
this.config.inclusive && this.config.inclusive.enabled ? `/${this.config.inclusive.route}` : '',
|
||||
this.config.names && this.config.names.enabled && this.config.names.published ? `/${this.config.names.route}` : '',
|
||||
this.config.people && this.config.people.enabled ? `/${this.config.people.route}` : '',
|
||||
`/${this.config.contact.team.route}`,
|
||||
this.config.workshops && this.config.workshops.enabled ? `/${this.config.workshops.route}` : '',
|
||||
this.config.terminology.enabled && this.config.terminology.published ? `/${this.config.terminology.route}` : '',
|
||||
this.config.calendar?.enabled ? `/${this.config.calendar.route}` : '',
|
||||
this.config.census.enabled ? `/${this.config.census.route}` : '',
|
||||
this.config.inclusive.enabled ? `/${this.config.inclusive.route}` : '',
|
||||
this.config.names.enabled && this.config.names.published ? `/${this.config.names.route}` : '',
|
||||
this.config.people.enabled ? `/${this.config.people.route}` : '',
|
||||
this.config.contact.enabled ? `/${this.config.contact.team.route}` : '',
|
||||
this.config.workshops?.enabled ? `/${this.config.workshops.route}` : '',
|
||||
];
|
||||
|
||||
if (this.config.community) {
|
||||
@ -396,13 +410,14 @@ export default {
|
||||
});
|
||||
|
||||
if (this.config.user.enabled) {
|
||||
const user = this.$user();
|
||||
links.push({
|
||||
link: `/${this.config.user.route}`,
|
||||
avatar: this.$user(),
|
||||
icon: 'user',
|
||||
text: this.user ? `@${this.user.username}` : this.$t('user.header'),
|
||||
textLong: this.user ? `@${this.user.username}` : this.$t('user.headerLong'),
|
||||
extra: ['/editor', this.$user() ? `/@${this.$user().username}` : null],
|
||||
extra: ['/editor', user ? `/@${user.username}` : null],
|
||||
});
|
||||
if (this.$isGranted('panel')) {
|
||||
links.push({
|
||||
@ -426,12 +441,12 @@ export default {
|
||||
|
||||
return links;
|
||||
},
|
||||
showCensus() {
|
||||
showCensus(): boolean {
|
||||
if (!process.client) {
|
||||
return false;
|
||||
}
|
||||
const finished = !!parseInt(window.localStorage.getItem(`census-${this.config.census.edition}-finished`) || 0);
|
||||
const dismissed = !!parseInt(window.localStorage.getItem(`census-${this.config.census.edition}-dismissed`) || 0);
|
||||
const finished = !!parseInt(window.localStorage.getItem(`census-${this.config.census.edition}-finished`) || '0');
|
||||
const dismissed = !!parseInt(window.localStorage.getItem(`census-${this.config.census.edition}-dismissed`) || '0');
|
||||
const alreadyIn = this.$route.path === `/${this.config.census.route}`;
|
||||
const isHomepage = this.$route.path === '/';
|
||||
if (!this.config.census.enabled || !isHomepage && (finished || dismissed) || this.censusDismissed || alreadyIn) {
|
||||
@ -439,7 +454,7 @@ export default {
|
||||
}
|
||||
const start = DateTime.fromISO(this.config.census.start).toLocal();
|
||||
const end = DateTime.fromISO(this.config.census.end).toLocal();
|
||||
const now = DateTime.utc().setZone(this.config.format.timezone);
|
||||
const now = DateTime.utc().setZone(this.config.format?.timezone ?? 'utc');
|
||||
return now >= start && now <= end;
|
||||
},
|
||||
},
|
||||
@ -453,29 +468,29 @@ export default {
|
||||
document.removeEventListener('scroll', this.updateShown);
|
||||
},
|
||||
methods: {
|
||||
isActiveRoute(link) {
|
||||
isActiveRoute(link: HeaderLink) {
|
||||
return decodeURIComponent(this.$route.path) === link.link ||
|
||||
link.extra && this.$route.name && link.extra.includes(this.$route.name.split(':')[0]) ||
|
||||
link.extra && this.$route.name && link.extra.includes((this.$route.name as string).split(':')[0]) ||
|
||||
(link.extra || []).includes(decodeURIComponent(this.$route.path)) ||
|
||||
(link.extra || []).filter((x) => x && (
|
||||
decodeURIComponent(this.$route.path).startsWith(`${x}/`) ||
|
||||
decodeURIComponent(this.$route.path).startsWith(`${x}:`))).length;
|
||||
},
|
||||
documentClicked() {
|
||||
documentClicked(): void {
|
||||
if (this.hamburgerActive) {
|
||||
this.hamburgerActive = false;
|
||||
}
|
||||
},
|
||||
updateShown() {
|
||||
const st = document.body.scrollTop || document.querySelector('html').scrollTop;
|
||||
updateShown(): void {
|
||||
const st = document.body.scrollTop || document.querySelector('html')!.scrollTop;
|
||||
this.hamburgerShown = st > 300;
|
||||
},
|
||||
dismissCensus() {
|
||||
dismissCensus(): void {
|
||||
window.localStorage.setItem(`census-${this.config.census.edition}-dismissed`, '1');
|
||||
this.censusDismissed = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -1,6 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const store = useMainStore();
|
||||
const darkMode = store.darkMode;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Submenu
|
||||
v-if="config.links.split"
|
||||
v-if="config.links.enabled && config.links.split"
|
||||
:links="[{
|
||||
name: 'links.links',
|
||||
route: config.links.route,
|
||||
@ -40,22 +49,3 @@
|
||||
}]"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'pinia';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
return {
|
||||
config: useConfig(),
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useMainStore, [
|
||||
'darkMode',
|
||||
]),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,3 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
link: string;
|
||||
locale: string;
|
||||
}>();
|
||||
|
||||
const config = useConfig();
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const homeLink = runtimeConfig.public.homeUrl;
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<nuxt-link v-if="locale === config.locale" :to="link" :class="$attrs.class">
|
||||
<slot></slot>
|
||||
@ -9,24 +21,3 @@
|
||||
<slot></slot>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
link: { required: true },
|
||||
locale: { required: true },
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
config: useConfig(),
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
homeLink: this.$config.public.home,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,19 +1,16 @@
|
||||
<template>
|
||||
<a v-if="isExternal" :href="to" target="_blank" rel="noopener" :class="$attrs.class"><slot></slot></a>
|
||||
<nuxt-link v-else :to="to">
|
||||
<slot></slot>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import type { RouteLocationRaw } from 'vue-router';
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
to: { required: true, type: String },
|
||||
},
|
||||
computed: {
|
||||
isExternal() {
|
||||
return this.to.startsWith('https://') || this.to.startsWith('http://');
|
||||
},
|
||||
},
|
||||
};
|
||||
const props = defineProps<{
|
||||
to: RouteLocationRaw,
|
||||
}>();
|
||||
|
||||
const isExternal = computed((): boolean => {
|
||||
return typeof props.to === 'string' && (props.to.startsWith('https://') || props.to.startsWith('http://'));
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<a v-if="isExternal" :href="to as string" target="_blank" rel="noopener" :class="$attrs.class"><slot></slot></a>
|
||||
<nuxt-link v-else :to="to"><slot></slot></nuxt-link>
|
||||
</template>
|
||||
|
@ -1,3 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
const { data: stats } = useFetch('/api/admin/stats-public', { lazy: true });
|
||||
|
||||
const runtimeConfig = useRuntimeConfig();
|
||||
const plausibleHost = runtimeConfig.public.plausibleHost;
|
||||
const heartbeatHost = runtimeConfig.public.heartbeatHost;
|
||||
|
||||
const overall = ref(true);
|
||||
|
||||
const activeStats = computed(() => {
|
||||
if (stats.value === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return overall
|
||||
? stats.value.overall
|
||||
: stats.value.current || {};
|
||||
});
|
||||
|
||||
const formatNumber = (number: number): string => {
|
||||
if (number > 1000000) {
|
||||
return `${Math.round(10 * number / 1000000) / 10}M`;
|
||||
}
|
||||
if (number > 1000) {
|
||||
return `${Math.round(10 * number / 1000) / 10}k`;
|
||||
}
|
||||
return number.toString();
|
||||
};
|
||||
|
||||
const formatDuration = (secondsCount: number): string => {
|
||||
const minutes = Math.floor(secondsCount / 60);
|
||||
const seconds = secondsCount % 60;
|
||||
const res = [];
|
||||
if (minutes > 0) {
|
||||
res.push(`${minutes}m`);
|
||||
}
|
||||
if (seconds > 0) {
|
||||
res.push(`${seconds}s`);
|
||||
}
|
||||
return res.join(' ');
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="stats !== null">
|
||||
<p class="h6 mb-2">
|
||||
@ -13,7 +56,7 @@
|
||||
|
|
||||
<strong><T>footer.stats.current</T></strong>
|
||||
</p>
|
||||
<ul :key="overall" class="list-unstyled">
|
||||
<ul :key="`${overall}`" class="list-unstyled">
|
||||
<li v-if="activeStats.cards" class="mb-2">
|
||||
<Icon v="id-card" />
|
||||
<T>footer.stats.keys.cards</T><T>quotation.colon</T>
|
||||
@ -67,55 +110,3 @@
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
setup() {
|
||||
const { data: stats } = useFetch('/api/admin/stats-public', { lazy: true });
|
||||
return {
|
||||
stats,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
plausibleHost: this.$config.public.plausibleHost,
|
||||
heartbeatHost: this.$config.public.heartbeatHost,
|
||||
overall: true,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
activeStats() {
|
||||
if (this.stats === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return this.overall
|
||||
? this.stats.overall
|
||||
: this.stats.current || {};
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
formatNumber(number) {
|
||||
if (number > 1000000) {
|
||||
return `${Math.round(10 * number / 1000000) / 10}M`;
|
||||
}
|
||||
if (number > 1000) {
|
||||
return `${Math.round(10 * number / 1000) / 10}k`;
|
||||
}
|
||||
return number;
|
||||
},
|
||||
formatDuration(secondsCount) {
|
||||
const minutes = Math.floor(secondsCount / 60);
|
||||
const seconds = secondsCount % 60;
|
||||
const res = [];
|
||||
if (minutes > 0) {
|
||||
res.push(`${minutes}m`);
|
||||
}
|
||||
if (seconds > 0) {
|
||||
res.push(`${seconds}s`);
|
||||
}
|
||||
return res.join(' ');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,3 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
interface Link {
|
||||
name: string;
|
||||
route: string;
|
||||
icon: string;
|
||||
iconInverse?: boolean;
|
||||
routesExtra?: string[];
|
||||
condition?: boolean;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
prefix?: string;
|
||||
links: Link[];
|
||||
extraClass?: string;
|
||||
}>(), {
|
||||
prefix: '',
|
||||
extraClass: '',
|
||||
});
|
||||
|
||||
const visibleLinks = computed((): Link[] => {
|
||||
return props.links.filter((l) => l.condition === undefined || l.condition);
|
||||
});
|
||||
|
||||
const buildRoute = (route: string): string => {
|
||||
return `${props.prefix}/${route}`;
|
||||
};
|
||||
|
||||
const route = useRoute();
|
||||
const isActiveRoute = (path: string, routesExtra?: string[]): boolean => {
|
||||
if (routesExtra && route.name && routesExtra.includes((route.name as string).split(':')[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let current = decodeURIComponent(route.fullPath).replace(/\/$/, '');
|
||||
if (current.includes('#')) {
|
||||
current = current.substring(0, current.indexOf('#'));
|
||||
}
|
||||
const expected = buildRoute(path).replace(/\/$/, '');
|
||||
|
||||
return current === expected || current.startsWith(`${expected}/`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section v-if="visibleLinks.length" class="mt-4 mt-lg-0 d-print-none">
|
||||
<div class="d-none d-md-inline-flex btn-group btn-block mb-2 w-100">
|
||||
@ -24,36 +67,3 @@
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
prefix: { default: '' },
|
||||
links: { required: true },
|
||||
extraClass: { default: '' },
|
||||
},
|
||||
computed: {
|
||||
visibleLinks() {
|
||||
return this.links.filter((l) => l.condition === undefined || l.condition === true);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
buildRoute(route) {
|
||||
return `${this.prefix}/${route}`;
|
||||
},
|
||||
isActiveRoute(route, routesExtra) {
|
||||
if (routesExtra && this.$route.name && routesExtra.includes(this.$route.name.split(':')[0])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let current = decodeURIComponent(this.$route.fullPath).replace(/\/$/, '');
|
||||
if (current.includes('#')) {
|
||||
current = current.substring(0, current.indexOf('#'));
|
||||
}
|
||||
const expected = this.buildRoute(route).replace(/\/$/, '');
|
||||
|
||||
return current === expected || current.startsWith(`${expected}/`);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,3 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import forbidden from '../src/forbidden.ts';
|
||||
|
||||
definePageMeta({
|
||||
translatedPaths: (config) => {
|
||||
if (!config.user.enabled) {
|
||||
return [];
|
||||
}
|
||||
return [`/${encodeURIComponent(config.user.termsRoute)}`];
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<h2>
|
||||
@ -49,25 +62,3 @@
|
||||
<p><T>terms.content.closing.changes</T></p>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import forbidden from '../src/forbidden.js';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
definePageMeta({
|
||||
translatedPaths: (config) => {
|
||||
if (!config.user.enabled) {
|
||||
return [];
|
||||
}
|
||||
return [`/${encodeURIComponent(config.user.termsRoute)}`];
|
||||
},
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
forbidden,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -1,3 +1,53 @@
|
||||
<script setup lang="ts">
|
||||
const config = useConfig();
|
||||
|
||||
interface Link {
|
||||
icon: string;
|
||||
header: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
const mainLinks: Link[] = [];
|
||||
if (config.pronouns.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'tags',
|
||||
header: 'pronouns.headerLong',
|
||||
route: config.pronouns.route,
|
||||
});
|
||||
}
|
||||
if (config.nouns.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'book',
|
||||
header: 'nouns.headerLong',
|
||||
route: config.nouns.route,
|
||||
});
|
||||
}
|
||||
if (config.user.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'id-card',
|
||||
header: 'profile.bannerButton',
|
||||
route: config.user.route,
|
||||
});
|
||||
}
|
||||
|
||||
const { data: latestPosts } = useAsyncData('latest-posts', async () => {
|
||||
if (!config.blog) {
|
||||
return [];
|
||||
}
|
||||
return (await $fetch('/api/blog')).slice(0, 9);
|
||||
}, {
|
||||
lazy: true,
|
||||
});
|
||||
const { data: featuredPosts } = useAsyncData('featured-posts', async () => {
|
||||
if (!config.blog?.shortcuts) {
|
||||
return [];
|
||||
}
|
||||
return await $fetch('/api/blog?shortcuts');
|
||||
}, {
|
||||
lazy: true,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Page>
|
||||
<AdPlaceholder :phkey="['header', null]" />
|
||||
@ -12,8 +62,12 @@
|
||||
</p>
|
||||
|
||||
<div class="row">
|
||||
<div v-for="{ icon, header, route, link } in mainLinks" :class="[mainLinks.length > 3 ? 'col-6 col-lg-3' : 'col', 'my-4', 'text-center']">
|
||||
<LocaleLink :link="link || route" :locale="link ? 'external' : config.locale">
|
||||
<div
|
||||
v-for="{ icon, header, route } in mainLinks"
|
||||
:key="header"
|
||||
:class="[mainLinks.length > 3 ? 'col-6 col-lg-3' : 'col', 'my-4', 'text-center']"
|
||||
>
|
||||
<LocaleLink :link="route" :locale="config.locale">
|
||||
<p>
|
||||
<Icon :v="icon" :size="2" class="d-inline-block d-lg-none" />
|
||||
<Icon :v="icon" :size="3" class="d-none d-lg-inline-block" />
|
||||
@ -115,78 +169,3 @@
|
||||
</section>
|
||||
</Page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapState } from 'pinia';
|
||||
import useConfig from '../composables/useConfig.ts';
|
||||
import { useMainStore } from '../store/index.ts';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const config = useConfig();
|
||||
|
||||
const { data: latestPosts } = useAsyncData('latest-posts', async () => {
|
||||
if (!config.blog) {
|
||||
return [];
|
||||
}
|
||||
return (await $fetch('/api/blog')).slice(0, 9);
|
||||
}, {
|
||||
lazy: true,
|
||||
});
|
||||
const { data: featuredPosts } = useAsyncData('featured-posts', async () => {
|
||||
if (!config.blog?.shortcuts) {
|
||||
return [];
|
||||
}
|
||||
return await $fetch('/api/blog?shortcuts');
|
||||
}, {
|
||||
lazy: true,
|
||||
});
|
||||
|
||||
return {
|
||||
config,
|
||||
latestPosts,
|
||||
featuredPosts,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(useMainStore, [
|
||||
'user',
|
||||
]),
|
||||
},
|
||||
data() {
|
||||
const mainLinks = [];
|
||||
if (this.config.pronouns.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'tags',
|
||||
header: 'pronouns.headerLong',
|
||||
route: this.config.pronouns.route,
|
||||
});
|
||||
}
|
||||
if (this.config.nouns.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'book',
|
||||
header: 'nouns.headerLong',
|
||||
route: this.config.nouns.route,
|
||||
});
|
||||
}
|
||||
if (this.config.user.enabled) {
|
||||
mainLinks.push({
|
||||
icon: 'id-card',
|
||||
header: 'profile.bannerButton',
|
||||
route: this.config.user.route,
|
||||
});
|
||||
}
|
||||
// if (this.config.locale === 'pl') {
|
||||
// mainLinks.push({
|
||||
// icon: 'isjp.svg',
|
||||
// header: 'isjp.homepage',
|
||||
// link: 'https://isjp.pl',
|
||||
// })
|
||||
// }
|
||||
|
||||
return {
|
||||
mainLinks,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -7,6 +7,7 @@ export interface User {
|
||||
avatarSource: string;
|
||||
bannedReason: string;
|
||||
usernameNorm: string;
|
||||
bannedTerms?: string;
|
||||
bannedBy: string;
|
||||
lastActive: number;
|
||||
banSnapshot: string;
|
||||
|
Loading…
x
Reference in New Issue
Block a user