Merge remote-tracking branch 'origin/main'

This commit is contained in:
Andrea Vos 2024-11-02 15:47:47 +01:00
commit 3e32c16658
67 changed files with 117 additions and 253 deletions

View File

@ -3,9 +3,9 @@
needs: []
rules:
-
if: $CI_COMMIT_REF_PROTECTED && $DEPLOY_TARGET =~ $ENVIRONMENT_PATTERN
if: $CI_COMMIT_REF_PROTECTED == 'true' && $DEPLOY_TARGET =~ $ENVIRONMENT_PATTERN
-
if: $CI_COMMIT_REF_PROTECTED
if: $CI_COMMIT_REF_PROTECTED == 'true'
when: manual
image: node:20.12.2
before_script:

View File

@ -1,66 +1,61 @@
<script setup lang="ts">
const { $translator: translator } = useNuxtApp();
const { data: circleMentions } = useFetch<Record<string, Record<string, string>>>(
'/api/profile/my-circle-mentions',
{ lazy: true },
);
const dialogue = useDialogue();
const removeSelf = async (username: string) => {
await dialogue.confirm(translator.translate('profile.circles.removeSelf.confirm', { username }), 'warning');
await dialogue.postWithAlertOnError(`/api/profile/remove-self-circle/${encodeURIComponent(username)}`);
window.location.reload();
};
</script>
<template>
<Loading :value="circleMentions">
<p class="small text-muted">
<T>profile.circles.yourMentions.description</T>
</p>
<table class="table table-striped table-bordered">
<tr v-if="!circleMentions || Object.keys(circleMentions).length === 0">
<td class="text-center">
<T>table.empty</T>
</td>
</tr>
<template v-else>
<template v-for="(profiles, username) in circleMentions">
<tr v-if="username !== 'null'">
<th>
<LocaleLink :link="`/@${username}`" locale="_">
@{{ username }}
</LocaleLink>
</th>
<td>
<ul>
<template v-for="(relationship, locale) in profiles">
<li v-if="locale && $locales[locale]">
<LocaleLink :link="`/@${username}`" :locale="locale">
{{ $locales[locale].name }}
</LocaleLink><T>quotation.colon</T>
{{ relationship }}
</li>
</template>
</ul>
</td>
<td style="max-width: 4rem">
<button class="btn btn-link btn-sm text-danger" @click="removeSelf(username)">
<Icon v="trash" />
<T>profile.circles.removeSelf.action</T>
</button>
</td>
</tr>
<tbody>
<tr v-if="!circleMentions || Object.keys(circleMentions).length === 0">
<td class="text-center">
<T>table.empty</T>
</td>
</tr>
<template v-else>
<template v-for="(profiles, username) in circleMentions" :key="username">
<tr v-if="username !== 'null'">
<th>
<LocaleLink :link="`/@${username}`" locale="_">
@{{ username }}
</LocaleLink>
</th>
<td>
<ul>
<template v-for="(relationship, locale) in profiles" :key="locale">
<li v-if="locale && $locales[locale]">
<LocaleLink :link="`/@${username}`" :locale="locale">
{{ $locales[locale].name }}
</LocaleLink><T>quotation.colon</T>
{{ relationship }}
</li>
</template>
</ul>
</td>
<td style="max-width: 4rem">
<button class="btn btn-link btn-sm text-danger" @click="removeSelf(username)">
<Icon v="trash" />
<T>profile.circles.removeSelf.action</T>
</button>
</td>
</tr>
</template>
</template>
</template>
</tbody>
</table>
</Loading>
</template>
<script>
import { useFetch } from 'nuxt/app';
import useDialogue from '../composables/useDialogue.ts';
export default {
setup() {
const { data: circleMentions } = useFetch('/api/profile/my-circle-mentions', { lazy: true });
return {
circleMentions,
dialogue: useDialogue(),
};
},
methods: {
async removeSelf(username) {
await this.dialogue.confirm(this.$t('profile.circles.removeSelf.confirm', { username }), 'warning');
await this.dialogue.postWithAlertOnError(`/api/profile/remove-self-circle/${encodeURIComponent(username)}`);
window.location.reload();
},
},
};
</script>

View File

@ -6,10 +6,8 @@
</template>
<script>
import DateLocal from '../data/calendar/DateLocal.vue';
export default {
components: { DateLocal },
components: { DateLocal: useLocaleComponent('calendar', 'DateLocal') },
props: {
day: { required: true },
inline: { type: Boolean },

View File

@ -35,7 +35,7 @@ const formattedPronounName = computed(() => {
</script>
<template>
<a :href="`${base}/${pronounWithoutDomain}`" class="badge bg-primary text-white mx-1">
<span class="badge bg-primary text-white mx-1">
{{ formattedPronounName }}
</a>
</span>
</template>

View File

@ -224,7 +224,6 @@ import { mapState } from 'pinia';
import type { RouteLocationRaw } from 'vue-router';
import useConfig from '../composables/useConfig.ts';
import NounsNav from '../data/nouns/NounsNav.vue';
import forbidden from '../src/forbidden.ts';
import type { User } from '../src/user.ts';
import { useMainStore } from '../store/index.ts';
@ -243,7 +242,7 @@ type HeaderLink = HeaderTo & {
};
export default defineComponent({
components: { NounsNav },
components: { NounsNav: useLocaleComponent('nouns', 'NounsNav') },
setup() {
const hoverItem = ref<HeaderLink | null>(null);
return {

View File

@ -1,10 +1,8 @@
<template>
<div>
<AdPlaceholder phkey="content-footer" />
<section>
<ShopifyEmbed />
</section>
</div>
<AdPlaceholder phkey="content-footer" />
<section v-bind="$attrs">
<ShopifyEmbed />
</section>
<!--
<section>

View File

@ -0,0 +1,11 @@
export default (module: string, componentName: string) => {
const config = useConfig();
return defineAsyncComponent(async () => {
try {
return await import(`~/locale/${config.locale}/${module}/${componentName}.vue`);
} catch (error) {
return () => {};
}
});
};

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -761,7 +761,7 @@ workshops:
Currently, we offer training and workshops <strong>online</strong>,
as well as in person in the Poland, Netherlands and UK (with reimbursement of travel costs); in English and in Polish.
- >
If you are interested, send us an email to <strong>{mailto:workshops@pronouns.page=workshops@pronouns.page}</strong>,
If you are interested, send us an email to {mailto:workshops@pronouns.page=workshops@pronouns.page},
remembering to provide information such as: type of organization, location, preferred date and form,
expected number of participants, which issues you would like to focus on, etc.
- >

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1414,7 +1414,7 @@ workshops:
Oferujemy szkolenia i warsztaty <strong>na terenie Polski</strong> (za zwrotem ewentualnych kosztów dojazdu),
a także <strong>online</strong>; w języku polskim i angielskim.
- >
Jeśli jesteś zainteresowanx, podeślij nam maila na adres <strong>{mailto:szkolenia@zaimki.pl=szkolenia@zaimki.pl}</strong>,
Jeśli jesteś zainteresowanx, podeślij nam maila na adres {mailto:szkolenia@zaimki.pl=szkolenia@zaimki.pl},
pamiętając o podaniu informacji takich jak: rodzaj organizacji, lokalizacja, preferowany termin i forma,
oczekiwana liczba osób mających wziąć udział, na których zagadnieniach chciałxbyś się skupić, itp.
- >

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -128,6 +128,7 @@ sources:
moderation: 'Пропозиції повинні бути схвалені перед публікацією.'
key: 'Ключ'
keyInfo: 'Ідентифікатор, що використовується для зв’язку джерел з різних мовних версій і зв’язку з словником.'
keyInfoConvention: 'В ідеалі: найпростіша форма терміна англійською, в нижньому регістрі.'
images: 'Зображення'
spoiler: 'Спойлер'
otherVersions: 'В інших мовах'
@ -793,6 +794,7 @@ profile:
removeSelf:
action: 'Прибрати себе'
confirm: 'Ви впевнені, що хочете прибрати себе з кола @%username%?'
add: 'Додайте людей в своє коло'
column: 'Колонка'
opinions:
custom: 'додане користувач_кою:'
@ -1358,6 +1360,7 @@ flags:
Trigender: 'Триґендерн{adjective}'
Trixic: 'Триксичн{adjective}'
Two_Spirit: 'Дві душі'
Unlabeled: 'Без лейблів'
Xenogender: 'Ксеноґендерн{adjective}'
calendar:
@ -1479,6 +1482,7 @@ calendar:
trans_history_month: 'Місяць трансгендерної історії'
aplatonic_visibility_day: 'День видимості аплатонічних'
aromantic_visibility_day: '{https://aromanticvisibilityday.org/=День видимості аромантичних}'
aromantic_spectrum_visibility_day: '{https://aromanticspectrumday.net/en/home-english=День видимості аромантичного спектру}'
unlabeled_day: 'День видимості людей без ярликів'
transition_week: 'Тиждень трансгендерного переходу'
black_hiv_awareness_day: 'День обізнаності про ВІЛ та СНІД у темношкірих людей'
@ -1851,3 +1855,13 @@ supportBanner:
Ось як ви можете допомогти невинним людям {https://supportukrainenow.org=в Україні},
{https://www.humanrightscareers.com/issues/charities-helping-civilians-in-palestine/=в Газі} та
{https://donate.unhcr.org/int/en/general=інших зонах воєнних конфліктів}.
form:
persistent:
present:
question: >
Здається, у вас є незбережені дані. На вашому девайсі збережено чернетку (%time%). Ви б хотіли її
відновити?
load: 'Відновити чернетку'
abandon: 'Відмовитися від чернетки'
saved: 'Чернетка збережена у вашому браузері (%time%)'

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,63 +0,0 @@
<template>
<section>
<div class="d-none d-md-inline-flex btn-group btn-block mb-2 w-100">
<router-link
v-for="{ name, icon, route } in links"
:key="name"
:to="buildRoute(route)"
:class="['btn', isActiveRoute(route) ? 'btn-primary' : 'btn-outline-primary']"
>
<Icon :v="icon" />
<T>nouns.{{ name }}.header</T>
</router-link>
</div>
<div class="d-block-force d-md-none btn-group-vertical btn-block mb-2 w-100">
<router-link
v-for="{ name, icon, route } in links"
:key="name"
:to="buildRoute(route)"
:class="['btn', isActiveRoute(route) ? 'btn-primary' : 'btn-outline-primary']"
>
<Icon :v="icon" />
<T>nouns.{{ name }}.header</T>
</router-link>
</div>
</section>
</template>
<script>
import useConfig from '~/composables/useConfig.ts';
export default {
setup() {
return {
config: useConfig(),
};
},
data() {
const links = [
{ name: 'nouns', icon: 'book', route: '' },
];
if (this.config.inclusive.enabled) {
links.push({ name: 'inclusive', icon: 'book-heart', route: this.config.inclusive.route });
}
if (this.config.terminology.enabled) {
links.push({ name: 'terms', icon: 'flag', route: this.config.terminology.route });
}
return {
links,
};
},
methods: {
buildRoute(route) {
return `/${route}`;
},
isActiveRoute(route) {
return decodeURIComponent(this.$route.fullPath).replace(/\/$/, '') === this.buildRoute(route).replace(/\/$/, '');
},
},
};
</script>

View File

@ -1 +0,0 @@
<template></template>

View File

@ -1,2 +0,0 @@
<template>
</template>

View File

@ -40,10 +40,9 @@ import type Dictionary from '~/components/Dictionary.vue';
import useConfig from '~/composables/useConfig.ts';
import useSimpleHead from '~/composables/useSimpleHead.ts';
import NounsExtra from '~/data/nouns/NounsExtra.vue';
import NounsNav from '~/data/nouns/NounsNav.vue';
export default defineComponent({
components: { NounsNav, NounsExtra },
components: { NounsNav: useLocaleComponent('nouns', 'NounsNav'), NounsExtra },
setup() {
definePageMeta({
translatedPaths: (config) => translatedPathByConfigModule(config.nouns),

View File

@ -46,13 +46,13 @@
<T>contact.team.credentials.description</T>
<ul>
<li v-for="credential in credentials">
<li v-for="credential in credentials" :key="credential.username">
<Spelling :text="convertName(credential.credentialsName || credential.teamName)" />
<a :href="`https://pronouns.page/@${credential.username}`" class="badge bg-light text-dark border">
@{{ credential.username }}
</a>
<ul>
<li v-for="item in credential.credentials">
<li v-for="item in credential.credentials" :key="item">
<ProfileLink v-if="item.startsWith('https://') || item.startsWith('http://')" :link="item" />
<LinkedText v-else :text="item" />
</li>
@ -145,9 +145,9 @@ interface Admin {
username: string;
teamName: string;
locale: string;
credentials: string[];
credentialsLevel: number;
credentialsName: string;
credentials: string[] | null;
credentialsLevel: number | null;
credentialsName: string | null;
}
export default defineComponent({
@ -162,51 +162,43 @@ export default defineComponent({
});
const { $translator: translator } = useNuxtApp();
const config = useConfig();
useSimpleHead({
title: translator.translate('contact.team.name'),
description: translator.translate('contact.team.description'),
}, translator);
const { convertName } = useSpelling();
const { data: membersByLocale } = useAsyncData(async () => {
const teamAsyncData = useAsyncData(async () => {
const membersByLocaleRaw = await $fetch<Record<string, Admin[]>>('/api/admin/list');
return Object.fromEntries(Object.entries(membersByLocaleRaw).filter(([_, members]) => {
const membersByLocale = Object.fromEntries(Object.entries(membersByLocaleRaw).filter(([_, members]) => {
return members.length > 0;
}));
const credentials = membersByLocale[config.locale]
.filter((member) => member.credentials !== null)
.toSorted((a, b) => {
if (a.credentialsLevel! > b.credentialsLevel!) {
return -1;
}
if (a.credentialsLevel! < b.credentialsLevel!) {
return 1;
}
return Math.random() > 0.5 ? 1 : -1;
});
return { membersByLocale, credentials };
});
return {
config: useConfig(),
config,
convertName,
membersByLocale,
membersByLocale: computed(() => teamAsyncData.data.value?.membersByLocale),
credentials: computed(() => teamAsyncData.data.value?.credentials),
};
},
computed: {
credentials(): Admin[] {
const r = [];
for (const locale in this.membersByLocale) {
if (!this.membersByLocale.hasOwnProperty(locale)) {
continue;
}
for (const member of this.membersByLocale[locale]) {
if (member.locale === this.config.locale && member.credentials !== null) {
r.push(member);
}
}
}
return r.sort((a, b) => {
if (a.credentialsLevel > b.credentialsLevel) {
return -1;
}
if (a.credentialsLevel < b.credentialsLevel) {
return 1;
}
return Math.random() > 0.5 ? 1 : -1;
});
},
},
});
</script>

View File

@ -16,7 +16,7 @@ const isBrowser = (userAgent: string | undefined): boolean => {
};
export default defineEventHandler((event) => {
if (process.env.NODE_ENV === 'production' &&
if (process.env.DISABLE_SSR &&
!event.path.startsWith('/card/@') && !event.path.startsWith('/blog.atom')) {
event.context.nuxt = event.context.nuxt ?? {};
event.context.nuxt.noSSR = isBrowser(getHeader(event, 'user-agent')) || isHighLoadTime(global.config.locale);