mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-21 16:33:48 +01:00
(add) server page
This commit is contained in:
parent
66c32554c5
commit
5a692f54e7
3
app.vue
3
app.vue
@ -54,6 +54,7 @@ const getLdJson = (additionalGraphes: Thing[] = []): string => {
|
||||
useHead((): Record<string, any> => ({
|
||||
htmlAttrs: {
|
||||
lang: locale.value,
|
||||
'data-bs-theme': colorMode.value,
|
||||
},
|
||||
title: getTitle(),
|
||||
meta: [
|
||||
@ -84,7 +85,7 @@ useHead((): Record<string, any> => ({
|
||||
}));
|
||||
</script>
|
||||
<template>
|
||||
<div class="text-slate-700 dark:text-slate-200 bg-slate-100 dark:bg-gray-900">
|
||||
<div class="text-slate-800 dark:text-slate-200 bg-slate-100 dark:bg-gray-900">
|
||||
<noscript class="block bg-accent-800 text-white text-center py-1.5 px-3 keep-all relative z-[10005]">Please turn on Javascript from your browser's settings.</noscript>
|
||||
<NuxtLayout>
|
||||
<NuxtPage />
|
||||
|
@ -9,23 +9,23 @@ export default [
|
||||
},
|
||||
{
|
||||
"lang": "es",
|
||||
"label": "español"
|
||||
"label": "Español"
|
||||
},
|
||||
{
|
||||
"lang": "es-419",
|
||||
"label": "español (Latinoamérica)"
|
||||
"label": "Español (Latinoamérica)"
|
||||
},
|
||||
{
|
||||
"lang": "fr",
|
||||
"label": "français"
|
||||
"label": "Français"
|
||||
},
|
||||
{
|
||||
"lang": "hr",
|
||||
"label": "hrvatski"
|
||||
"label": "Hrvatski"
|
||||
},
|
||||
{
|
||||
"lang": "it",
|
||||
"label": "italiano"
|
||||
"label": "Italiano"
|
||||
},
|
||||
{
|
||||
"lang": "nl",
|
||||
@ -33,15 +33,15 @@ export default [
|
||||
},
|
||||
{
|
||||
"lang": "pl",
|
||||
"label": "polski"
|
||||
"label": "Polski"
|
||||
},
|
||||
{
|
||||
"lang": "pt-BR",
|
||||
"label": "português (Brasil)"
|
||||
"label": "Português (Brasil)"
|
||||
},
|
||||
{
|
||||
"lang": "pt-PT",
|
||||
"label": "português (Portugal)"
|
||||
"label": "Português (Portugal)"
|
||||
},
|
||||
{
|
||||
"lang": "vi",
|
||||
|
14
assets/js/misc/index.ts
Normal file
14
assets/js/misc/index.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export function resolveObjPath(o: object, s: string): any {
|
||||
s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
|
||||
s = s.replace(/^\./, ''); // strip a leading dot
|
||||
var a = s.split('.');
|
||||
for (var i = 0, n = a.length; i < n; ++i) {
|
||||
var k = a[i];
|
||||
if (k in o) {
|
||||
o = o[k];
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
<footer>
|
||||
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
@ -1,38 +1,15 @@
|
||||
<template>
|
||||
<nav class="sticky top-0 z-[9900] md:relative container mx-auto max-w-screen-xl h-16 lg:h-20 grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4">
|
||||
<div class="">
|
||||
<GNuxtLink :to="localePath('/')" class="flex items-center space-x-2 hover:opacity-80">
|
||||
<MiIcon class="h-8 w-8" />
|
||||
<div class="font-title font-bold text-lg">{{ $t('_seo.siteName') }}</div>
|
||||
</GNuxtLink>
|
||||
</div>
|
||||
<ul class="hidden lg:col-span-4 lg:space-x-8 xl:space-x-10 lg:flex justify-center">
|
||||
<li v-for="item in NavData.center">
|
||||
<GNuxtLink :to="localePath(item.to)" :class="['rounded-full px-4 py-1.5 hover:bg-slate-300 dark:hover:bg-slate-700 hover:bg-opacity-50 bg-blend-multiply', { 'bg-slate-200 dark:bg-slate-800': path.includes(item.to) }]">
|
||||
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
|
||||
<template v-else>
|
||||
{{ $t(item.i18n) }}
|
||||
</template>
|
||||
<div :class="['bg-slate-100 dark:bg-gray-900 bg-opacity-80 backdrop-blur-lg sticky top-0 z-[9900]', { 'shadow': scrollPos >= 40 }]">
|
||||
<nav class="container mx-auto max-w-screen-xl h-16 lg:h-20 grid items-center grid-cols-2 md:grid-cols-4 lg:grid-cols-6 p-4">
|
||||
<div class="">
|
||||
<GNuxtLink :to="localePath('/')" class="flex items-center space-x-2 hover:opacity-80">
|
||||
<MiIcon class="h-8 w-8" />
|
||||
<div class="font-title font-bold text-lg">{{ $t('_seo.siteName') }}</div>
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
<div>
|
||||
<ul class="hidden lg:col-span-4 lg:space-x-4 lg:flex justify-center">
|
||||
<li class="relative group">
|
||||
<a class="hover:opacity-80" href="#"><I18nIcon class="h-5 w-5" /><span class="sr-only">{{ $t('_nav.switchLang') }}</span></a>
|
||||
<div class="absolute top-6 right-0 hidden group-hover:block z-[9955]">
|
||||
<ul class="px-4 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg shadow-lg space-y-1">
|
||||
<li v-for="locale in locales">
|
||||
<GNuxtLink :to="switchLocalePath(locale.code)" class="hover:text-accent-600">
|
||||
{{ locale.name }}
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="border-l"></li>
|
||||
<li v-for="item in NavData.right">
|
||||
<GNuxtLink :to="item.to" class="hover:opacity-80">
|
||||
</div>
|
||||
<ul class="hidden lg:col-span-4 lg:space-x-8 xl:space-x-10 lg:flex justify-center">
|
||||
<li v-for="item in NavData.center">
|
||||
<GNuxtLink :to="localePath(item.to)" :class="['rounded-full px-4 py-1.5 hover:bg-slate-300 dark:hover:bg-slate-800 hover:bg-opacity-50 bg-blend-multiply', { 'bg-slate-200 dark:bg-slate-800': path.includes(item.to) }]">
|
||||
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
|
||||
<template v-else>
|
||||
{{ $t(item.i18n) }}
|
||||
@ -40,17 +17,82 @@
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<div>
|
||||
<ul class="hidden lg:col-span-4 lg:space-x-4 lg:flex justify-center">
|
||||
<li>
|
||||
<button class="hover:opacity-80" @click="rotateColorMode()" aria-label="Change Color Mode">
|
||||
<ClientOnly>
|
||||
<SunIcon class="h-5 w-5" v-if="colorMode.preference === 'light'" />
|
||||
<MoonIcon class="h-5 w-5" v-else-if="colorMode.preference === 'dark'" />
|
||||
<DisplayIcon class="h-5 w-5" v-else />
|
||||
</ClientOnly>
|
||||
</button>
|
||||
</li>
|
||||
<li class="relative group">
|
||||
<a class="hover:opacity-80" href="#"><I18nIcon class="h-5 w-5" /><span class="sr-only">{{ $t('_nav.switchLang') }}</span></a>
|
||||
<div class="absolute top-6 right-0 hidden group-hover:block z-[9955]">
|
||||
<ul class="px-4 py-2 bg-slate-50 dark:bg-slate-800 rounded-lg shadow-lg space-y-1">
|
||||
<li v-for="locale in locales">
|
||||
<GNuxtLink :to="switchLocalePath(locale.code)" :class="['hover:text-accent-600 py-1', {'text-accent-600 font-bold': currentLocale === locale.code}]">
|
||||
{{ locale.name }}
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="border-l"></li>
|
||||
<li v-for="item in NavData.right">
|
||||
<GNuxtLink :to="item.to" class="hover:opacity-80">
|
||||
<component v-if="item.icon" :is="item.icon" class="h-5 w-5" />
|
||||
<template v-else>
|
||||
{{ $t(item.i18n) }}
|
||||
</template>
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
|
||||
import I18nIcon from 'bi/translate.svg';
|
||||
import SunIcon from 'bi/sun.svg';
|
||||
import MoonIcon from 'bi/moon-stars.svg';
|
||||
import DisplayIcon from 'bi/display.svg';
|
||||
import NavData from '@/assets/data/nav';
|
||||
|
||||
const { locales } = useI18n();
|
||||
const { locales, locale: currentLocale } = useI18n();
|
||||
const { path } = useRoute();
|
||||
const switchLocalePath = useSwitchLocalePath();
|
||||
const localePath = useLocalePath();
|
||||
|
||||
const colorMode = useColorMode();
|
||||
function rotateColorMode() {
|
||||
const values = ['system', 'light', 'dark'];
|
||||
const index = values.indexOf(colorMode.preference);
|
||||
const next = (index + 1) % values.length;
|
||||
|
||||
colorMode.preference = values[next];
|
||||
}
|
||||
|
||||
const scrollPos = ref(0);
|
||||
|
||||
function updatePos() {
|
||||
scrollPos.value = window.screenY;
|
||||
}
|
||||
onMounted(() => {
|
||||
if (process.client) {
|
||||
window.addEventListener('scroll', updatePos());
|
||||
window.addEventListener('resize', updatePos());
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (process.client) {
|
||||
window.removeEventListener('scroll', updatePos());
|
||||
window.removeEventListener('resize', updatePos());
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -69,7 +69,7 @@ import { vFadeIn } from '@/assets/js/fadein';
|
||||
}
|
||||
|
||||
.item > .content {
|
||||
@apply p-5 bg-white dark:bg-slate-700 rounded-xl;
|
||||
@apply p-5 bg-white dark:bg-slate-800 rounded-xl;
|
||||
}
|
||||
|
||||
.item .img {
|
||||
|
@ -38,7 +38,7 @@ const localePath = useLocalePath();
|
||||
|
||||
<style scoped>
|
||||
.item {
|
||||
@apply rounded-2xl py-12 px-4 text-center bg-white dark:bg-slate-700;
|
||||
@apply rounded-2xl py-12 px-4 text-center bg-white dark:bg-slate-800;
|
||||
}
|
||||
|
||||
.find > .item {
|
||||
@ -54,10 +54,10 @@ const localePath = useLocalePath();
|
||||
}
|
||||
|
||||
.item > .link {
|
||||
@apply px-6 py-3 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors font-bold text-lg;
|
||||
@apply px-6 py-3 rounded-lg bg-gray-100 dark:bg-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700 transition-colors font-bold text-lg;
|
||||
}
|
||||
|
||||
.find .link {
|
||||
@apply text-accent-600 bg-white hover:bg-accent-100;
|
||||
@apply text-accent-600 dark:text-slate-200 bg-white dark:bg-accent-950 hover:bg-accent-100 dark:hover:bg-accent-900;
|
||||
}
|
||||
</style>
|
@ -18,12 +18,21 @@
|
||||
</ul>
|
||||
<div>
|
||||
<ul class="hidden lg:col-span-4 lg:space-x-4 lg:flex justify-center">
|
||||
<li>
|
||||
<button class="text-white hover:opacity-80" @click="rotateColorMode()" aria-label="Change Color Mode">
|
||||
<ClientOnly>
|
||||
<SunIcon class="h-5 w-5" v-if="colorMode.preference === 'light'" />
|
||||
<MoonIcon class="h-5 w-5" v-else-if="colorMode.preference === 'dark'" />
|
||||
<DisplayIcon class="h-5 w-5" v-else />
|
||||
</ClientOnly>
|
||||
</button>
|
||||
</li>
|
||||
<li class="relative group">
|
||||
<a class="text-white hover:opacity-80" href="#"><I18nIcon class="h-5 w-5" /><span class="sr-only">{{ $t('_nav.switchLang') }}</span></a>
|
||||
<div class="absolute top-6 right-0 hidden group-hover:block z-[9955]">
|
||||
<ul class="px-4 py-2 bg-slate-50 dark:bg-slate-700 rounded-lg shadow-lg space-y-1">
|
||||
<ul class="px-4 py-2 bg-slate-50 dark:bg-slate-800 rounded-lg shadow-lg space-y-2">
|
||||
<li v-for="locale in locales">
|
||||
<GNuxtLink :to="switchLocalePath(locale.code)" class="hover:text-accent-600">
|
||||
<GNuxtLink :to="switchLocalePath(locale.code)" :class="['hover:text-accent-600 py-1', {'text-accent-600 font-bold': currentLocale === locale.code}]">
|
||||
{{ locale.name }}
|
||||
</GNuxtLink>
|
||||
</li>
|
||||
@ -48,9 +57,21 @@
|
||||
<script setup>
|
||||
import MiIcon from '@/assets/svg/misskey_mi_bi.svg';
|
||||
import I18nIcon from 'bi/translate.svg';
|
||||
import SunIcon from 'bi/sun.svg';
|
||||
import MoonIcon from 'bi/moon-stars.svg';
|
||||
import DisplayIcon from 'bi/display.svg';
|
||||
import NavData from '@/assets/data/nav';
|
||||
|
||||
const { locales } = useI18n();
|
||||
const { locales, locale: currentLocale } = useI18n();
|
||||
const switchLocalePath = useSwitchLocalePath();
|
||||
const localePath = useLocalePath();
|
||||
|
||||
const colorMode = useColorMode();
|
||||
function rotateColorMode() {
|
||||
const values = ['system', 'light', 'dark'];
|
||||
const index = values.indexOf(colorMode.preference);
|
||||
const next = (index + 1) % values.length;
|
||||
|
||||
colorMode.preference = values[next];
|
||||
}
|
||||
</script>
|
||||
|
22
components/index/Sponsors.vue
Normal file
22
components/index/Sponsors.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="mb-12 text-2xl lg:text-3xl text-center font-bold font-title">{{ $t('_landing._sponsors.title') }}</h2>
|
||||
<div class="mx-auto max-w-[240px] space-y-8 [&>*]:block">
|
||||
<!-- スポンサーを入れるときは、リンクとアイコンを適当に並べるだけでいい感じになります -->
|
||||
<NuxtLink to="https://rss3.io/" target="_blank">
|
||||
<img src="/img/sponsors/rss3.svg" />
|
||||
</NuxtLink>
|
||||
<NuxtLink to="https://www.dotchain.ltd/advirth" target="_blank">
|
||||
<img src="/img/sponsors/dcadvirth.png" />
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -8,7 +8,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Loader } from '@/assets/js/particles/loader';
|
||||
import tailwindConfig from 'tailwindcss/resolveConfig';
|
||||
import resolveConfig from 'tailwindcss/resolveConfig';
|
||||
|
||||
const colorMode = useColorMode();
|
||||
const container = ref<HTMLElement>();
|
||||
@ -16,9 +16,11 @@ const isMounted = ref<boolean>(false);
|
||||
const particleEnabled = ref<boolean>(true);
|
||||
|
||||
const colorVars = computed<string>(() => {
|
||||
const out = [`--c-brand: ${tailwindConfig.theme?.extend.colors.accent['600'] || '#86b300'}`]
|
||||
if (colorMode.preference == 'dark') {
|
||||
out.join(`--c-text: `)
|
||||
const out = [`--c-brand: #86b300;`];
|
||||
if (colorMode.value == 'dark') {
|
||||
out.push(`--c-text: rgb(226 232 240);`);
|
||||
} else {
|
||||
out.push(`--c-text: rgb(51 65 85);`);
|
||||
}
|
||||
return out.join(' ');
|
||||
});
|
||||
@ -27,9 +29,11 @@ onMounted(() => {
|
||||
isMounted.value = true;
|
||||
});
|
||||
|
||||
let loader: Loader;
|
||||
|
||||
watch(container, (to) => {
|
||||
if (isMounted.value && process.client && to) {
|
||||
const loader = new Loader(to);
|
||||
loader = new Loader(to);
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
particleEnabled.value = false;
|
||||
@ -38,14 +42,15 @@ watch(container, (to) => {
|
||||
once: true,
|
||||
});
|
||||
|
||||
|
||||
onUnmounted(() => {
|
||||
setTimeout(() => {
|
||||
loader.destroy();
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
setTimeout(() => {
|
||||
loader.destroy();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,6 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
<div class="border border-gray-300 dark:border-gray-800 dark:bg-slate-800 rounded-lg shadow-lg overflow-hidden focus-within:ring-2 ring-accent-500 ring-offset-2">
|
||||
<NuxtLink :to="`https://${instance.url}`" target="_blank">
|
||||
<div class="relative aspect-video bg-gray-200 dark:bg-gray-600">
|
||||
<img v-if="instance.banner" :src="`https://instanceapp.misskey.page/instance-banners/${instance.url}.webp`" class="w-full h-full object-cover" />
|
||||
<div class="absolute h-1/2 bottom-0 left-0 w-full bg-gradient-to-b from-transparent to-black text-white p-4 flex items-end">
|
||||
<div class="h-14 w-14 min-w-0 flex-shrink-0 mr-4">
|
||||
<img v-if="instance.icon" :src="`https://instanceapp.misskey.page/instance-icons/${instance.url}.webp`" class="w-full h-full" />
|
||||
</div>
|
||||
<div class="min-w-0 flex flex-col justify-end">
|
||||
<h2 class="font-bold text-2xl whitespace-nowrap truncate">{{ instance.name }}</h2>
|
||||
<p class="opacity-90 text-sm truncate">{{ instance.url }} / v.{{ instance.nodeinfo?.software.version }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<p class="description h-12 mb-2">{{ instance.description }}</p>
|
||||
<div class="grid grid-cols-3 text-center">
|
||||
<dl>
|
||||
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.notes') }}</dt>
|
||||
<dd class="font-bold text-accent-600">{{ instance.stats?.originalNotesCount.toLocaleString() }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt class="text-xs opacity-90">{{ $t('_servers._statistics.users') }}</dt>
|
||||
<dd class="font-bold text-accent-600">{{ instance.stats?.originalUsersCount.toLocaleString() }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt class="text-xs opacity-90">{{ $t('_servers._registerAcceptance.title') }}</dt>
|
||||
<dd class="font-bold text-accent-600">{{ instance.meta?.disableRegistration ? $t('_servers._registerAcceptance.inviteOnly') : $t('_servers._registerAcceptance.public')}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -13,5 +43,12 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.description {
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
}
|
||||
</style>
|
@ -4,7 +4,7 @@ const isNavOpen = ref<boolean>(false);
|
||||
<template>
|
||||
<div>
|
||||
<GNav @toggleNav="isNavOpen = !isNavOpen" :is-open="isNavOpen" />
|
||||
<div class="main-content overflow-x-hidden">
|
||||
<div class="main-content">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<GFooter />
|
||||
|
@ -73,6 +73,8 @@ _landing:
|
||||
_donation:
|
||||
title: "寄付のお願い"
|
||||
description: "Misskeyは非営利なため、開発資金は皆様からの寄付に頼っています。Misskeyを気に入られたら、今後も開発を続けられるようにぜひ支援をお願いします。"
|
||||
_sponsors:
|
||||
title: "スポンサー"
|
||||
_servers:
|
||||
title: "サーバー一覧"
|
||||
description: "Misskeyは単一のサービスではなく、各々がサービスを提供する分散型ネットワークとなっています。Misskeyを利用するには、サービスを提供しているサーバーでアカウントを作成する必要があります。"
|
||||
@ -87,14 +89,15 @@ _servers:
|
||||
_search:
|
||||
title: "絞り込み検索"
|
||||
all: "すべて"
|
||||
query: "キーワードで検索"
|
||||
lang: "言語"
|
||||
orderBy: "並び替え"
|
||||
recomendded: "デフォルト"
|
||||
notesCount: "ノート数"
|
||||
usersCount: "ユーザー数"
|
||||
_registerAcceptance:
|
||||
title: "新規登録"
|
||||
public: "開放"
|
||||
inviteOnly: "招待のみ"
|
||||
|
||||
|
||||
_registerAcceptance:
|
||||
title: "新規登録"
|
||||
public: "開放"
|
||||
inviteOnly: "招待のみ"
|
||||
_list:
|
||||
showMore: "もっと見る"
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="relative min-h-full">
|
||||
<div class="relative min-h-full pb-24">
|
||||
<IndexHeroBg />
|
||||
<IndexHeroParticles />
|
||||
<IndexNav />
|
||||
@ -16,6 +16,7 @@
|
||||
<IndexGetStarted id="getStarted" />
|
||||
<GDots class="w-[95%] mx-auto text-accent-600" :space="30" />
|
||||
<IndexDonation />
|
||||
<IndexSponsors />
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -29,51 +29,71 @@
|
||||
</div>
|
||||
</template>
|
||||
</GHero>
|
||||
<div class="mt-12 pt-6 bg-white dark:bg-slate-950">
|
||||
<div class="my-12 pt-6 bg-white dark:bg-slate-950">
|
||||
<div class="container mx-auto max-w-screen-xl px-6 grid server-list gap-8">
|
||||
<aside class="hidden lg:block">
|
||||
<div class="sticky top-6 py-2 space-y-4">
|
||||
<h3 class="text-xl font-bold">絞り込み検索</h3>
|
||||
<div class="sticky top-24 py-2 space-y-4">
|
||||
<h3 class="text-xl font-bold">{{ $t('_servers._search.title') }}</h3>
|
||||
<form @submit.prevent="() => { f_query = f_query_partial }">
|
||||
<label class="form-label" for="query">{{ $t('_servers._search.query') }}</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="search" autocomplete="off" id="query" v-model="f_query_partial" />
|
||||
<button type="submit" class="btn btn-outline-primary hover:!text-white">
|
||||
<SearchIco class="stroke-[0.5] stroke-current" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div>
|
||||
<label class="form-label" for="languages">{{ $t('_servers._search.lang') }}</label>
|
||||
<select id="languages" class="form-select">
|
||||
<select id="languages" v-model="f_langs" class="form-select">
|
||||
<option :value="null">{{ $t('_servers._search.all') }}</option>
|
||||
<option v-for="lang in langs" :value="lang.lang">{{ lang.label }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="form-label" for="orderBy">{{ $t('_servers._search.orderBy') }}</label>
|
||||
<select id="orderBy" class="form-select">
|
||||
<option value="recomendded">{{ $t('_servers._search.recomendded') }}</option>
|
||||
<option value="notesCount">{{ $t('_servers._search.notesCount') }}</option>
|
||||
<option value="usersCount">{{ $t('_servers._search.usersCount') }}</option>
|
||||
</select>
|
||||
<div class="input-group">
|
||||
<select id="orderBy" v-model="f_orderBy" class="form-select">
|
||||
<option value="recomendded">{{ $t('_servers._search.recomendded') }}</option>
|
||||
<option value="notesCount">{{ $t('_servers._search.notesCount') }}</option>
|
||||
<option value="usersCount">{{ $t('_servers._search.usersCount') }}</option>
|
||||
</select>
|
||||
<button class="btn btn-outline-primary hover:!text-white" @click="switchOrder()">
|
||||
<SortDownIco v-if="f_order === 'desc'" class="stroke-[0.5] stroke-current" />
|
||||
<SortUpIco v-else class="stroke-[0.5] stroke-current" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="mb-1">{{ $t('_servers._search._registerAcceptance.title') }}</div>
|
||||
<div class="mb-1">{{ $t('_servers._registerAcceptance.title') }}</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" :value="null" id="registerAcceptance0">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" v-model="f_registerAcceptance" :value="null" id="registerAcceptance0">
|
||||
<label class="form-check-label" for="registerAcceptance0">
|
||||
{{ $t('_servers._search.all') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" value="public" id="registerAcceptance1">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" v-model="f_registerAcceptance" value="public" id="registerAcceptance1">
|
||||
<label class="form-check-label" for="registerAcceptance1">
|
||||
{{ $t('_servers._search._registerAcceptance.public') }}
|
||||
{{ $t('_servers._registerAcceptance.public') }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" value="inviteOnly" id="registerAcceptance2">
|
||||
<input class="form-check-input" type="radio" name="registerAcceptance" v-model="f_registerAcceptance" value="inviteOnly" id="registerAcceptance2">
|
||||
<label class="form-check-label" for="registerAcceptance2">
|
||||
{{ $t('_servers._search._registerAcceptance.inviteOnly') }}
|
||||
{{ $t('_servers._registerAcceptance.inviteOnly') }}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="grid gap-4 grid-cols-1 lg:grid-cols-3 xl:grid-cols-4">
|
||||
|
||||
<div>
|
||||
<div class="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2">
|
||||
<ServersItem v-for="item in filteredInstances.slice(0, f_limit)" :instance="item" />
|
||||
<button v-if="f_limit < filteredInstances.length" @click="f_limit += 20" class="btn btn-outline-primary btn-lg hover:!text-white block sm:col-span-2 md:col-span-2 lg:col-span-2 px-4">
|
||||
<ArrowIco class="mr-1" />{{ $t('_servers._list.showMore') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -81,12 +101,59 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InstanceInfo } from '@/types/instances-info';
|
||||
import SearchIco from 'bi/search.svg';
|
||||
import SortUpIco from 'bi/sort-down-alt.svg';
|
||||
import SortDownIco from 'bi/sort-down.svg';
|
||||
import ArrowIco from 'bi/arrow-down-circle.svg';
|
||||
|
||||
import type { InstanceInfo, InstanceItem } from '@/types/instances-info';
|
||||
import { resolveObjPath } from '@/assets/js/misc';
|
||||
import langs from '@/assets/data/lang';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
// ▼フォームデータ初期化▼
|
||||
type MiHubSFStorage = {
|
||||
f_langs: string;
|
||||
f_orderBy: 'recomendded' | 'notesCount' | 'usersCount';
|
||||
f_order: 'asc' | 'desc';
|
||||
f_registerAcceptance: 'public' | 'inviteOnly' | null;
|
||||
};
|
||||
|
||||
let savedSettings: MiHubSFStorage | null = null;
|
||||
if (process.client) {
|
||||
savedSettings = JSON.parse(window.localStorage.getItem('miHub_server_finder') ?? 'null') as MiHubSFStorage | null;
|
||||
}
|
||||
|
||||
const f_query_partial = ref<string>("");
|
||||
|
||||
const f_query = ref<string>("");
|
||||
const f_langs = ref<MiHubSFStorage['f_langs']>(savedSettings?.f_langs ?? locale.value);
|
||||
const f_orderBy = ref<MiHubSFStorage['f_orderBy']>(savedSettings?.f_orderBy ?? 'recomendded');
|
||||
const f_order = ref<MiHubSFStorage['f_order']>(savedSettings?.f_order ?? 'desc');
|
||||
const f_registerAcceptance = ref<MiHubSFStorage['f_registerAcceptance']>(savedSettings?.f_registerAcceptance || null);
|
||||
|
||||
const f_limit = ref<number>(20);
|
||||
// ▲フォームデータ初期化▲
|
||||
|
||||
// ▼フォームデータ保存処理▼
|
||||
watch([f_langs, f_orderBy, f_order, f_registerAcceptance], (to, from) => {
|
||||
f_limit.value = 20;
|
||||
|
||||
const newSettings: MiHubSFStorage = {
|
||||
f_langs: to[0],
|
||||
f_orderBy: to[1],
|
||||
f_order: to[2],
|
||||
f_registerAcceptance: to[3],
|
||||
};
|
||||
|
||||
if (process.client) {
|
||||
window.localStorage.setItem('miHub_server_finder', JSON.stringify(newSettings));
|
||||
}
|
||||
});
|
||||
// ▲フォームデータ保存処理▲
|
||||
|
||||
route.meta.title = t('_servers.title');
|
||||
route.meta.description = t('_servers.description');
|
||||
|
||||
@ -96,6 +163,58 @@ const { data } = await useFetch<InstanceInfo>('https://instanceapp.misskey.page/
|
||||
}
|
||||
});
|
||||
|
||||
const filteredInstances = computed<InstanceItem[]>(() => {
|
||||
let instances = data.value?.instancesInfos ?? [];
|
||||
|
||||
if (instances.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (f_query.value) {
|
||||
instances = instances.filter((instance) => instance.name.includes(f_query.value) || instance.description?.includes(f_query.value));
|
||||
}
|
||||
if (f_langs.value) {
|
||||
instances = instances.filter((instance) => instance.langs.includes(f_langs.value));
|
||||
}
|
||||
if (f_registerAcceptance.value) {
|
||||
instances = instances.filter((instance) => {
|
||||
if (f_registerAcceptance.value === 'inviteOnly') {
|
||||
return instance.meta?.disableRegistration;
|
||||
} else {
|
||||
return !instance.meta?.disableRegistration ?? true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (f_orderBy.value) {
|
||||
instances.sort((a, b) => {
|
||||
let orderKey: string;
|
||||
|
||||
switch(f_orderBy.value) {
|
||||
case 'recomendded':
|
||||
orderKey = 'value';
|
||||
break;
|
||||
case 'notesCount':
|
||||
orderKey = 'stats.originalNotesCount';
|
||||
break;
|
||||
case 'usersCount':
|
||||
orderKey = 'stats.originalUsersCount';
|
||||
break;
|
||||
}
|
||||
|
||||
if (f_order.value === 'desc') {
|
||||
return resolveObjPath(a, orderKey) > resolveObjPath(b, orderKey) ? -1 : 1;
|
||||
} else {
|
||||
return resolveObjPath(a, orderKey) < resolveObjPath(b, orderKey) ? -1 : 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return instances;
|
||||
});
|
||||
|
||||
function switchOrder() {
|
||||
f_order.value = f_order.value === 'asc' ? 'desc' : 'asc';
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
12
public/fonts/fonts.css
vendored
12
public/fonts/fonts.css
vendored
@ -1,4 +1,16 @@
|
||||
/** Japanese (GenjyuuGothicX) */
|
||||
@import url('./GenJyuuGothicX-Bold/GenJyuuGothicX-Bold.css');
|
||||
@import url('./GenJyuuGothicX-P-Bold/GenJyuuGothicX-P-Bold.css');
|
||||
@import url('./GenJyuuGothicX-P-Regular/GenJyuuGothicX-P-Regular.css');
|
||||
@import url('./GenJyuuGothicX-Regular/GenJyuuGothicX-Regular.css');
|
||||
/** Korean (Pretendard) */
|
||||
@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.8/dist/web/static/pretendard-dynamic-subset.css");
|
||||
|
||||
html[lang='ja'] {
|
||||
--mi-localized-font: 'GenJyuuGothicX';
|
||||
--mi-localized-font-p: 'GenJyuuGothicXP';
|
||||
}
|
||||
|
||||
html[lang='ko'] {
|
||||
--mi-localized-font: 'Pretendard';
|
||||
}
|
BIN
public/img/sponsors/dcadvirth.png
Normal file
BIN
public/img/sponsors/dcadvirth.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 182 KiB |
1
public/img/sponsors/rss3.svg
Normal file
1
public/img/sponsors/rss3.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 884.33 884.33"><defs><style>.cls-1{fill:#fff;}.cls-2{fill:#0b70ff;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="图层_1" data-name="图层 1"><circle class="cls-1" cx="442.16" cy="442.16" r="442.16"/><path d="M566,456.24c-8.5-12.61-29-19.34-43.67-23.3-1.71-.45-3.49-.9-5.38-1.37l-.14,0c-7.91-2-16.87-4.21-23.85-8-8-4.38-11.93-10.06-11.93-17.36a17.71,17.71,0,0,1,.33-3.53l.05-.21c.11-.52.25-1.05.41-1.59l.07-.21,0-.08a20.46,20.46,0,0,1,5.06-8c5.93-6,15.4-9.37,26-9.37h.34a56,56,0,0,1,22.56,5.31,61,61,0,0,1,18.81,13.34L554.8,417v.17H568V371.4H554.8v10.38a.46.46,0,0,1-.2.39c-.12,0-.28-.09-.36-.17a67.94,67.94,0,0,0-18.4-9.91,71,71,0,0,0-22-3.82c-8.58,0-15.6,1-21.47,2.91A41.42,41.42,0,0,0,479,378.61,35,35,0,0,0,469,391.24a35.83,35.83,0,0,0-3.45,15.18,31.8,31.8,0,0,0,1.15,9.21,34.13,34.13,0,0,0,4.49,9.29,9.21,9.21,0,0,0,.55.79l.28.38.11.16c8.67,11.92,28,17.24,42.08,21.13l.38.11,3.13.86,3.4.91a138.94,138.94,0,0,1,21.47,7.17,25.41,25.41,0,0,1,9.36,7.08,22,22,0,0,1,4.59,9.83,21.52,21.52,0,0,1-.79,10.77A27.35,27.35,0,0,1,549,494.66a32,32,0,0,1-11.42,6.51,47.65,47.65,0,0,1-12.67,2.27,63.86,63.86,0,0,1-20.42-2.31c-11.24-3.56-21.52-10.43-28.18-18.82a2.71,2.71,0,0,1-.48-.92l-.13-13.08v-.17H462.5V516h13.21V502A57.47,57.47,0,0,0,494.55,513a78.07,78.07,0,0,0,25.27,4.82h1.07c7.32,0,24.48,0,37.92-11.39A38.91,38.91,0,0,0,567.9,495a40,40,0,0,0,4.25-13.38,37.44,37.44,0,0,0-.62-13.51A31.06,31.06,0,0,0,566,456.24Z"/><path d="M435.69,456.23c-8.34-12.56-28.91-19.31-43.67-23.29-1.68-.44-3.42-.87-5.27-1.32-16-4-35.93-8.89-36.16-25.46a17.52,17.52,0,0,1,1.59-7.27,21.78,21.78,0,0,1,4.51-6.4c6-5.92,15.42-9.32,25.94-9.32H383a55.78,55.78,0,0,1,22.54,5.31,59.83,59.83,0,0,1,18.7,13.34l.26,15.21v.17h13.2V371.4H424.47v10.38c0,.14-.08.34-.2.38s-.21,0-.36-.16a67.81,67.81,0,0,0-18.4-9.91,71,71,0,0,0-22-3.82c-8.63,0-15.67,1-21.52,2.91a41.45,41.45,0,0,0-13.29,7.43,35,35,0,0,0-10.06,12.63,35.85,35.85,0,0,0-3.46,15.18,32.27,32.27,0,0,0,1.14,9.21,35.44,35.44,0,0,0,4.37,9.29c8.29,12.92,28.58,18.47,43.39,22.53l.34.09,2.94.81,3.1.83.3.08a138.94,138.94,0,0,1,21.47,7.17,25.84,25.84,0,0,1,9.4,7.13,22.09,22.09,0,0,1,4.61,9.92,21.55,21.55,0,0,1-.81,10.78,27.2,27.2,0,0,1-6.73,10.53,31.22,31.22,0,0,1-11.37,6.49,47.21,47.21,0,0,1-12.68,2.22c-.9,0-1.83.07-2.77.07A65.27,65.27,0,0,1,374,501.13c-.67-.21-1.35-.44-2-.68l-.11,0c-1.35-.48-2.7-1-4-1.6A60.43,60.43,0,0,1,350.26,487l-.26-.25c-.81-.79-1.61-1.65-2.4-2.54l-.6-.68c-.34-.41-.69-.82-1-1.24a2.71,2.71,0,0,1-.47-.91l-.13-13.08v-.17H332V516h13.35V502A57.47,57.47,0,0,0,364.22,513a78.07,78.07,0,0,0,25.27,4.82h1.06c7.29,0,24.36,0,37.93-11.39A39.07,39.07,0,0,0,437.57,495a40,40,0,0,0,4.25-13.38,37.44,37.44,0,0,0-.62-13.51A31.17,31.17,0,0,0,435.69,456.23Z"/><path d="M300.3,504.67l-36.09-54.55A39.75,39.75,0,0,0,282,446.68c7.89-4,14.23-9.08,18.12-16.58a43.67,43.67,0,0,0,4.77-20,41.62,41.62,0,0,0-12-29.21c-8.31-7.57-18.71-11.26-31.8-11.26H191.13v15.1H206v120.1H189.85v12.62h46.84V504.85H221.6V450.34h25.25l35.36,54.51H266.09v12.62h50.43V504.85H300.8A.64.64,0,0,1,300.3,504.67ZM221.6,384.06h38.68c11.88,0,21,4.72,25.62,12.83a26.83,26.83,0,0,1-.43,26.47c-4.73,8.05-10.33,12.42-22.75,12.42H221.6Z"/><path d="M692.61,473.77c0-18.12-9.16-31.2-24.34-36.81,12.9-6.78,18.56-17.88,18.56-29.33,0-26.41-22.17-40.43-51.08-40.43-15.79.12-32.65,5.38-44.1,21.5l12.05,7.83c6.38-8.65,15.54-15.31,32-15.31,21,0,35.9,8.53,35.9,24.78,0,15.54-14.94,24.18-30.24,24.18H620.57v15h22c19.52,0,34.94,10.51,34.45,28.39-1.08,19.05-17.82,29.21-39.75,29.21-14.09,0-29.76-4.44-41.44-16.94l-.61-.23L585,496.78l.49.12c14.69,13.79,32.16,20.92,51.8,20.92C667.55,517.82,692.61,501.46,692.61,473.77Z"/><rect class="cls-2" x="142.14" y="435.68" width="37.98" height="15.51"/><rect class="cls-2" x="708.4" y="435.68" width="37.98" height="15.51"/></g></g></svg>
|
After Width: | Height: | Size: 3.7 KiB |
@ -31,9 +31,9 @@ export default <Config> {
|
||||
},
|
||||
},
|
||||
fontFamily: {
|
||||
'title': ['Capriola', 'GenJyuuGothicX', ...defaultTheme.fontFamily.sans],
|
||||
'sans': ['Nunito', 'GenJyuuGothicX', ...defaultTheme.fontFamily.sans],
|
||||
'content-sans': ['Nunito', 'GenJyuuGothicXP', ...defaultTheme.fontFamily.sans],
|
||||
'title': ['Capriola', 'var(--mi-localized-font, \'\')', ...defaultTheme.fontFamily.sans],
|
||||
'sans': ['Nunito', 'var(--mi-localized-font, \'\')', ...defaultTheme.fontFamily.sans],
|
||||
'content-sans': ['Nunito', 'var(--mi-localized-font-p, var(--mi-localized-font))', ...defaultTheme.fontFamily.sans],
|
||||
}
|
||||
},
|
||||
plugins: [],
|
||||
|
@ -19,11 +19,11 @@ export type InstanceItem = {
|
||||
/** Icon Image existance */
|
||||
icon: boolean;
|
||||
/** nodeinfo */
|
||||
nodeinfo: Object | null,
|
||||
nodeinfo: Record<string, any> | null,
|
||||
/** result of api/meta */
|
||||
meta: Object | null,
|
||||
meta: Record<string, any> | null,
|
||||
|
||||
stats?: Object, // deprecated (result of api/stats)
|
||||
stats?: Record<string, any>, // deprecated (result of api/stats)
|
||||
};
|
||||
|
||||
/** JSON Object Returned from `joinmisskey/api`. */
|
||||
|
Loading…
Reference in New Issue
Block a user