forked from FoundKeyGang/FoundKey
client: consolidate about & notifications pages
Reviewed-on: FoundKeyGang/FoundKey#104 Changelog: Changed
This commit is contained in:
commit
039f6db40a
15 changed files with 213 additions and 300 deletions
|
@ -847,6 +847,7 @@ typeToConfirm: "Please enter {x} to confirm"
|
||||||
deleteAccount: "Delete account"
|
deleteAccount: "Delete account"
|
||||||
numberOfPageCache: "Number of cached pages"
|
numberOfPageCache: "Number of cached pages"
|
||||||
numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
|
numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
|
||||||
|
document: "Document"
|
||||||
file: "File"
|
file: "File"
|
||||||
unclip: "Unclip"
|
unclip: "Unclip"
|
||||||
confirmToUnclipAlreadyClippedNote: "This note is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?"
|
confirmToUnclipAlreadyClippedNote: "This note is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?"
|
||||||
|
|
|
@ -854,6 +854,7 @@ typeToConfirm: "この操作を行うには {x} と入力してください"
|
||||||
deleteAccount: "アカウント削除"
|
deleteAccount: "アカウント削除"
|
||||||
numberOfPageCache: "ページキャッシュ数"
|
numberOfPageCache: "ページキャッシュ数"
|
||||||
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
|
numberOfPageCacheDescription: "多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。"
|
||||||
|
document: "ドキュメント"
|
||||||
|
|
||||||
_emailUnavailable:
|
_emailUnavailable:
|
||||||
used: "既に使用されています"
|
used: "既に使用されています"
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<div class="sub">
|
<div class="sub">
|
||||||
<a v-click-anime href="https://misskey-hub.net/help.html" target="_blank" @click.passive="close()">
|
<button v-click-anime class="_button" @click="help">
|
||||||
<i class="fas fa-question-circle icon"></i>
|
<i class="fas fa-question-circle icon"></i>
|
||||||
<div class="text">{{ i18n.ts.help }}</div>
|
<div class="text">{{ i18n.ts.help }}</div>
|
||||||
</a>
|
</button>
|
||||||
<MkA v-click-anime to="/about" @click.passive="close()">
|
<MkA v-click-anime to="/about" @click.passive="close()">
|
||||||
<i class="fas fa-info-circle icon"></i>
|
<i class="fas fa-info-circle icon"></i>
|
||||||
<div class="text">{{ i18n.t('aboutX', { x: instanceName }) }}</div>
|
<div class="text">{{ i18n.ts.instanceInfo }}</div>
|
||||||
</MkA>
|
</MkA>
|
||||||
<MkA v-click-anime to="/about-misskey" @click.passive="close()">
|
<MkA v-click-anime to="/about-misskey" @click.passive="close()">
|
||||||
<img src="/static-assets/favicon.png" class="icon"/>
|
<img src="/static-assets/favicon.png" class="icon"/>
|
||||||
|
@ -40,6 +40,7 @@ import { instanceName } from '@/config';
|
||||||
import { defaultStore } from '@/store';
|
import { defaultStore } from '@/store';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { deviceKind } from '@/scripts/device-kind';
|
import { deviceKind } from '@/scripts/device-kind';
|
||||||
|
import * as os from '@/os';
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
src?: HTMLElement;
|
src?: HTMLElement;
|
||||||
|
@ -72,6 +73,28 @@ const items = Object.keys(menuDef).filter(k => !menu.includes(k)).map(k => menuD
|
||||||
function close() {
|
function close() {
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function help(ev: MouseEvent) {
|
||||||
|
os.popupMenu([{
|
||||||
|
type: 'link',
|
||||||
|
to: '/mfm-cheat-sheet',
|
||||||
|
text: i18n.ts._mfm.cheatSheet,
|
||||||
|
icon: 'fas fa-code',
|
||||||
|
}, {
|
||||||
|
type: 'link',
|
||||||
|
to: '/scratchpad',
|
||||||
|
text: i18n.ts.scratchpad,
|
||||||
|
icon: 'fas fa-terminal',
|
||||||
|
}, null, {
|
||||||
|
text: i18n.ts.document,
|
||||||
|
icon: 'fas fa-question-circle',
|
||||||
|
action: () => {
|
||||||
|
window.open('https://misskey-hub.net/help.html', '_blank');
|
||||||
|
},
|
||||||
|
}], ev.currentTarget ?? ev.target);
|
||||||
|
|
||||||
|
close();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -112,14 +112,14 @@ export const menuDef = reactive({
|
||||||
icon: 'fas fa-at',
|
icon: 'fas fa-at',
|
||||||
show: computed(() => $i != null),
|
show: computed(() => $i != null),
|
||||||
indicated: computed(() => $i != null && $i.hasUnreadMentions),
|
indicated: computed(() => $i != null && $i.hasUnreadMentions),
|
||||||
to: '/my/mentions',
|
to: '/my/notifications#mentions',
|
||||||
},
|
},
|
||||||
messages: {
|
messages: {
|
||||||
title: 'directNotes',
|
title: 'directNotes',
|
||||||
icon: 'fas fa-envelope',
|
icon: 'fas fa-envelope',
|
||||||
show: computed(() => $i != null),
|
show: computed(() => $i != null),
|
||||||
indicated: computed(() => $i != null && $i.hasUnreadSpecifiedNotes),
|
indicated: computed(() => $i != null && $i.hasUnreadSpecifiedNotes),
|
||||||
to: '/my/messages',
|
to: '/my/notifications#directNotes',
|
||||||
},
|
},
|
||||||
favorites: {
|
favorites: {
|
||||||
title: 'favorites',
|
title: 'favorites',
|
||||||
|
@ -151,17 +151,12 @@ export const menuDef = reactive({
|
||||||
federation: {
|
federation: {
|
||||||
title: 'federation',
|
title: 'federation',
|
||||||
icon: 'fas fa-globe',
|
icon: 'fas fa-globe',
|
||||||
to: '/federation',
|
to: '/about#federation',
|
||||||
},
|
},
|
||||||
emojis: {
|
emojis: {
|
||||||
title: 'emojis',
|
title: 'emojis',
|
||||||
icon: 'fas fa-laugh',
|
icon: 'fas fa-laugh',
|
||||||
to: '/emojis',
|
to: '/about#emojis',
|
||||||
},
|
|
||||||
scratchpad: {
|
|
||||||
title: 'scratchpad',
|
|
||||||
icon: 'fas fa-terminal',
|
|
||||||
to: '/scratchpad',
|
|
||||||
},
|
},
|
||||||
ui: {
|
ui: {
|
||||||
title: 'switchUi',
|
title: 'switchUi',
|
||||||
|
|
106
packages/client/src/pages/about.federation.vue
Normal file
106
packages/client/src/pages/about.federation.vue
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
<template>
|
||||||
|
<div class="taeiyria">
|
||||||
|
<div class="query">
|
||||||
|
<MkInput v-model="host" :debounce="true" class="">
|
||||||
|
<template #prefix><i class="fas fa-search"></i></template>
|
||||||
|
<template #label>{{ $ts.host }}</template>
|
||||||
|
</MkInput>
|
||||||
|
<FormSplit style="margin-top: var(--margin);">
|
||||||
|
<MkSelect v-model="state">
|
||||||
|
<template #label>{{ $ts.state }}</template>
|
||||||
|
<option value="all">{{ $ts.all }}</option>
|
||||||
|
<option value="federating">{{ $ts.federating }}</option>
|
||||||
|
<option value="subscribing">{{ $ts.subscribing }}</option>
|
||||||
|
<option value="publishing">{{ $ts.publishing }}</option>
|
||||||
|
<option value="suspended">{{ $ts.suspended }}</option>
|
||||||
|
<option value="blocked">{{ $ts.blocked }}</option>
|
||||||
|
<option value="notResponding">{{ $ts.notResponding }}</option>
|
||||||
|
</MkSelect>
|
||||||
|
<MkSelect v-model="sort">
|
||||||
|
<template #label>{{ $ts.sort }}</template>
|
||||||
|
<option value="+pubSub">{{ $ts.pubSub }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-pubSub">{{ $ts.pubSub }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+notes">{{ $ts.notes }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-notes">{{ $ts.notes }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+users">{{ $ts.users }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-users">{{ $ts.users }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+following">{{ $ts.following }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option>
|
||||||
|
<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option>
|
||||||
|
</MkSelect>
|
||||||
|
</FormSplit>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
||||||
|
<div class="dqokceoi">
|
||||||
|
<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Last communicated: ${new Date(instance.lastCommunicatedAt).toLocaleString()}\nStatus: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`">
|
||||||
|
<MkInstanceCardMini :instance="instance"/>
|
||||||
|
</MkA>
|
||||||
|
</div>
|
||||||
|
</MkPagination>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import MkButton from '@/components/ui/button.vue';
|
||||||
|
import MkInput from '@/components/form/input.vue';
|
||||||
|
import MkSelect from '@/components/form/select.vue';
|
||||||
|
import MkPagination from '@/components/ui/pagination.vue';
|
||||||
|
import MkInstanceCardMini from '@/components/instance-card-mini.vue';
|
||||||
|
import FormSplit from '@/components/form/split.vue';
|
||||||
|
import * as os from '@/os';
|
||||||
|
import { i18n } from '@/i18n';
|
||||||
|
|
||||||
|
let host = $ref('');
|
||||||
|
let state = $ref('federating');
|
||||||
|
let sort = $ref('+pubSub');
|
||||||
|
const pagination = {
|
||||||
|
endpoint: 'federation/instances' as const,
|
||||||
|
limit: 10,
|
||||||
|
offsetMode: true,
|
||||||
|
params: computed(() => ({
|
||||||
|
sort: sort,
|
||||||
|
host: host !== '' ? host : null,
|
||||||
|
...(
|
||||||
|
state === 'federating' ? { federating: true } :
|
||||||
|
state === 'subscribing' ? { subscribing: true } :
|
||||||
|
state === 'publishing' ? { publishing: true } :
|
||||||
|
state === 'suspended' ? { suspended: true } :
|
||||||
|
state === 'blocked' ? { blocked: true } :
|
||||||
|
state === 'notResponding' ? { notResponding: true } :
|
||||||
|
{}),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
function getStatus(instance) {
|
||||||
|
if (instance.isSuspended) return 'Suspended';
|
||||||
|
if (instance.isBlocked) return 'Blocked';
|
||||||
|
if (instance.isNotResponding) return 'Error';
|
||||||
|
return 'Alive';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.taeiyria {
|
||||||
|
> .query {
|
||||||
|
background: var(--bg);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dqokceoi {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||||
|
grid-gap: 12px;
|
||||||
|
|
||||||
|
> .instance:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -73,7 +73,7 @@
|
||||||
<MkSpacer v-else-if="tab === 'federation'" :content-max="1000" :margin-min="20">
|
<MkSpacer v-else-if="tab === 'federation'" :content-max="1000" :margin-min="20">
|
||||||
<XFederation/>
|
<XFederation/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<MkSpacer v-else-if="tab === 'charts'" :content-max="1000" :margin-min="20">
|
<MkSpacer v-else-if="tab === 'charts'" :content-max="1200" :margin-min="20">
|
||||||
<MkInstanceStats :chart-limit="500" :detailed="true"/>
|
<MkInstanceStats :chart-limit="500" :detailed="true"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
|
@ -81,6 +81,8 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import XEmojis from './about.emojis.vue';
|
||||||
|
import XFederation from './about.federation.vue';
|
||||||
import { version, host } from '@/config';
|
import { version, host } from '@/config';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import FormSection from '@/components/form/section.vue';
|
import FormSection from '@/components/form/section.vue';
|
||||||
|
@ -93,6 +95,23 @@ import number from '@/filters/number';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
|
const headerTabs = $computed(() => [{
|
||||||
|
key: 'overview',
|
||||||
|
title: i18n.ts.overview,
|
||||||
|
}, {
|
||||||
|
key: 'emojis',
|
||||||
|
title: i18n.ts.customEmojis,
|
||||||
|
icon: 'fas fa-laugh',
|
||||||
|
}, {
|
||||||
|
key: 'federation',
|
||||||
|
title: i18n.ts.federation,
|
||||||
|
icon: 'fas fa-globe',
|
||||||
|
}, {
|
||||||
|
key: 'charts',
|
||||||
|
title: i18n.ts.charts,
|
||||||
|
icon: 'fas fa-chart-simple',
|
||||||
|
}]);
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
initialTab?: string;
|
initialTab?: string;
|
||||||
}>(), {
|
}>(), {
|
||||||
|
@ -100,7 +119,7 @@ const props = withDefaults(defineProps<{
|
||||||
});
|
});
|
||||||
|
|
||||||
let stats = $ref(null);
|
let stats = $ref(null);
|
||||||
let tab = $ref(props.initialTab);
|
let tab = $ref(headerTabs.some(({ key }) => key === props.initialTab) ? props.initialTab : 'overview');
|
||||||
|
|
||||||
const initStats = () => os.api('stats', {
|
const initStats = () => os.api('stats', {
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
|
@ -109,15 +128,6 @@ const initStats = () => os.api('stats', {
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
const headerActions = $computed(() => []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [{
|
|
||||||
key: 'overview',
|
|
||||||
title: i18n.ts.overview,
|
|
||||||
}, {
|
|
||||||
key: 'charts',
|
|
||||||
title: i18n.ts.charts,
|
|
||||||
icon: 'fas fa-chart-bar',
|
|
||||||
}]);
|
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: i18n.ts.instanceInfo,
|
title: i18n.ts.instanceInfo,
|
||||||
icon: 'fas fa-info-circle',
|
icon: 'fas fa-info-circle',
|
||||||
|
|
|
@ -185,12 +185,10 @@ const menuDef = $computed(() => [{
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
const component = $computed(() => {
|
const component = $computed(() => {
|
||||||
if (props.initialPage == null) return null;
|
|
||||||
switch (props.initialPage) {
|
switch (props.initialPage) {
|
||||||
case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
|
case 'overview': return defineAsyncComponent(() => import('./overview.vue'));
|
||||||
case 'users': return defineAsyncComponent(() => import('./users.vue'));
|
case 'users': return defineAsyncComponent(() => import('./users.vue'));
|
||||||
case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
|
case 'emojis': return defineAsyncComponent(() => import('./emojis.vue'));
|
||||||
case 'federation': return defineAsyncComponent(() => import('../federation.vue'));
|
|
||||||
case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
|
case 'queue': return defineAsyncComponent(() => import('./queue.vue'));
|
||||||
case 'files': return defineAsyncComponent(() => import('./files.vue'));
|
case 'files': return defineAsyncComponent(() => import('./files.vue'));
|
||||||
case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
|
case 'announcements': return defineAsyncComponent(() => import('./announcements.vue'));
|
||||||
|
@ -204,6 +202,7 @@ const component = $computed(() => {
|
||||||
case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
|
case 'integrations': return defineAsyncComponent(() => import('./integrations.vue'));
|
||||||
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
|
case 'instance-block': return defineAsyncComponent(() => import('./instance-block.vue'));
|
||||||
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
case 'proxy-account': return defineAsyncComponent(() => import('./proxy-account.vue'));
|
||||||
|
default: return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,58 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<div :class="$style.root">
|
|
||||||
<XCategory/>
|
|
||||||
</div>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref, computed } from 'vue';
|
|
||||||
import XCategory from './emojis.category.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
function menu(ev) {
|
|
||||||
os.popupMenu([{
|
|
||||||
icon: 'fas fa-download',
|
|
||||||
text: i18n.ts.export,
|
|
||||||
action: async () => {
|
|
||||||
os.api('export-custom-emojis', {
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
os.alert({
|
|
||||||
type: 'info',
|
|
||||||
text: i18n.ts.exportRequested,
|
|
||||||
});
|
|
||||||
}).catch((err) => {
|
|
||||||
os.alert({
|
|
||||||
type: 'error',
|
|
||||||
text: err.message,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}], ev.currentTarget ?? ev.target);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
|
||||||
icon: 'fas fa-ellipsis-h',
|
|
||||||
handler: menu,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.customEmojis,
|
|
||||||
icon: 'fas fa-laugh',
|
|
||||||
bg: 'var(--bg)',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
|
||||||
.root {
|
|
||||||
max-width: 1000px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,122 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :content-max="1000">
|
|
||||||
<div class="taeiyria">
|
|
||||||
<div class="query">
|
|
||||||
<MkInput v-model="host" :debounce="true" class="">
|
|
||||||
<template #prefix><i class="fas fa-search"></i></template>
|
|
||||||
<template #label>{{ i18n.ts.host }}</template>
|
|
||||||
</MkInput>
|
|
||||||
<FormSplit style="margin-top: var(--margin);">
|
|
||||||
<MkSelect v-model="state">
|
|
||||||
<template #label>{{ i18n.ts.state }}</template>
|
|
||||||
<option value="all">{{ i18n.ts.all }}</option>
|
|
||||||
<option value="federating">{{ i18n.ts.federating }}</option>
|
|
||||||
<option value="subscribing">{{ i18n.ts.subscribing }}</option>
|
|
||||||
<option value="publishing">{{ i18n.ts.publishing }}</option>
|
|
||||||
<option value="suspended">{{ i18n.ts.suspended }}</option>
|
|
||||||
<option value="blocked">{{ i18n.ts.blocked }}</option>
|
|
||||||
<option value="notResponding">{{ i18n.ts.notResponding }}</option>
|
|
||||||
</MkSelect>
|
|
||||||
<MkSelect v-model="sort">
|
|
||||||
<template #label>{{ i18n.ts.sort }}</template>
|
|
||||||
<option value="+pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-pubSub">{{ i18n.ts.pubSub }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+notes">{{ i18n.ts.notes }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-notes">{{ i18n.ts.notes }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+users">{{ i18n.ts.users }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-users">{{ i18n.ts.users }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+following">{{ i18n.ts.following }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-following">{{ i18n.ts.following }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+followers">{{ i18n.ts.followers }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-followers">{{ i18n.ts.followers }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+caughtAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-caughtAt">{{ i18n.ts.registeredAt }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
<option value="+lastCommunicatedAt">{{ i18n.ts.lastCommunication }} ({{ i18n.ts.descendingOrder }})</option>
|
|
||||||
<option value="-lastCommunicatedAt">{{ i18n.ts.lastCommunication }} ({{ i18n.ts.ascendingOrder }})</option>
|
|
||||||
</MkSelect>
|
|
||||||
</FormSplit>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
|
||||||
<div class="dqokceoi">
|
|
||||||
<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Last communicated: ${new Date(instance.lastCommunicatedAt).toLocaleString()}\nStatus: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`">
|
|
||||||
<MkInstanceCardMini :instance="instance"/>
|
|
||||||
</MkA>
|
|
||||||
</div>
|
|
||||||
</MkPagination>
|
|
||||||
</div>
|
|
||||||
</MkSpacer>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { computed } from 'vue';
|
|
||||||
import MkButton from '@/components/ui/button.vue';
|
|
||||||
import MkInput from '@/components/form/input.vue';
|
|
||||||
import MkSelect from '@/components/form/select.vue';
|
|
||||||
import MkPagination from '@/components/ui/pagination.vue';
|
|
||||||
import MkInstanceCardMini from '@/components/instance-card-mini.vue';
|
|
||||||
import FormSplit from '@/components/form/split.vue';
|
|
||||||
import * as os from '@/os';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
let host = $ref('');
|
|
||||||
let state = $ref('federating');
|
|
||||||
let sort = $ref('+pubSub');
|
|
||||||
const pagination = {
|
|
||||||
endpoint: 'federation/instances' as const,
|
|
||||||
limit: 10,
|
|
||||||
offsetMode: true,
|
|
||||||
params: computed(() => ({
|
|
||||||
sort,
|
|
||||||
host: host !== '' ? host : null,
|
|
||||||
...(
|
|
||||||
state === 'federating' ? { federating: true } :
|
|
||||||
state === 'subscribing' ? { subscribing: true } :
|
|
||||||
state === 'publishing' ? { publishing: true } :
|
|
||||||
state === 'suspended' ? { suspended: true } :
|
|
||||||
state === 'blocked' ? { blocked: true } :
|
|
||||||
state === 'notResponding' ? { notResponding: true } :
|
|
||||||
{}),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
|
|
||||||
function getStatus(instance) {
|
|
||||||
if (instance.isSuspended) return 'Suspended';
|
|
||||||
if (instance.isBlocked) return 'Blocked';
|
|
||||||
if (instance.isNotResponding) return 'Error';
|
|
||||||
return 'Alive';
|
|
||||||
}
|
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.federation,
|
|
||||||
icon: 'fas fa-globe',
|
|
||||||
bg: 'var(--bg)',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.taeiyria {
|
|
||||||
> .query {
|
|
||||||
background: var(--bg);
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dqokceoi {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
|
||||||
grid-gap: 12px;
|
|
||||||
|
|
||||||
> .instance:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :content-max="800">
|
|
||||||
<XNotes :pagination="pagination"/>
|
|
||||||
</MkSpacer>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import XNotes from '@/components/notes.vue';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
const pagination = {
|
|
||||||
endpoint: 'notes/mentions' as const,
|
|
||||||
limit: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.mentions,
|
|
||||||
icon: 'fas fa-at',
|
|
||||||
bg: 'var(--bg)',
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -1,32 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :content-max="800">
|
|
||||||
<XNotes :pagination="pagination"/>
|
|
||||||
</MkSpacer>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import XNotes from '@/components/notes.vue';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
|
|
||||||
const pagination = {
|
|
||||||
endpoint: 'notes/mentions' as const,
|
|
||||||
limit: 10,
|
|
||||||
params: {
|
|
||||||
visibility: 'specified',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const headerActions = $computed(() => []);
|
|
||||||
|
|
||||||
const headerTabs = $computed(() => []);
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.directNotes,
|
|
||||||
icon: 'fas fa-envelope',
|
|
||||||
bg: 'var(--bg)',
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -2,8 +2,14 @@
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="800">
|
<MkSpacer :content-max="800">
|
||||||
<div class="clupoqwt">
|
<div v-if="tab === 'mentions'">
|
||||||
<XNotifications class="notifications" :include-types="includeTypes" :unread-only="tab === 'unread'"/>
|
<XNotes :pagination="mentionsPagination"/>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="tab === 'directNotes'">
|
||||||
|
<XNotes :pagination="directNotesPagination"/>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<XNotifications class="notifications" :include-types="includeTypes" :unread-only="unreadOnly"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
|
@ -13,14 +19,51 @@
|
||||||
import { computed } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { notificationTypes } from 'foundkey-js';
|
import { notificationTypes } from 'foundkey-js';
|
||||||
import XNotifications from '@/components/notifications.vue';
|
import XNotifications from '@/components/notifications.vue';
|
||||||
|
import XNotes from '@/components/notes.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
|
|
||||||
let tab = $ref('all');
|
const headerTabs = $computed(() => [{
|
||||||
let includeTypes = $ref<string[] | null>(null);
|
key: 'all',
|
||||||
|
title: i18n.ts.all,
|
||||||
|
}, {
|
||||||
|
key: 'unread',
|
||||||
|
title: i18n.ts.unread,
|
||||||
|
}, {
|
||||||
|
key: 'mentions',
|
||||||
|
title: i18n.ts.mentions,
|
||||||
|
icon: 'fas fa-at',
|
||||||
|
}, {
|
||||||
|
key: 'directNotes',
|
||||||
|
title: i18n.ts.directNotes,
|
||||||
|
icon: 'fas fa-envelope',
|
||||||
|
}]);
|
||||||
|
|
||||||
function setFilter(ev) {
|
const props = withDefaults(defineProps<{
|
||||||
|
initialTab?: string;
|
||||||
|
}>(), {
|
||||||
|
initialTab: 'all',
|
||||||
|
});
|
||||||
|
|
||||||
|
let tab = $ref(headerTabs.some(({ key }) => key === props.initialTab) ? props.initialTab : 'all');
|
||||||
|
let includeTypes = $ref<string[] | null>(null);
|
||||||
|
let unreadOnly = $computed(() => tab === 'unread');
|
||||||
|
|
||||||
|
const mentionsPagination = {
|
||||||
|
endpoint: 'notes/mentions' as const,
|
||||||
|
limit: 10,
|
||||||
|
};
|
||||||
|
|
||||||
|
const directNotesPagination = {
|
||||||
|
endpoint: 'notes/mentions' as const,
|
||||||
|
limit: 10,
|
||||||
|
params: {
|
||||||
|
visibility: 'specified',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function setFilter(ev: Event): void {
|
||||||
const typeItems = notificationTypes.map(t => ({
|
const typeItems = notificationTypes.map(t => ({
|
||||||
text: i18n.t(`_notification._types.${t}`),
|
text: i18n.t(`_notification._types.${t}`),
|
||||||
active: includeTypes && includeTypes.includes(t),
|
active: includeTypes && includeTypes.includes(t),
|
||||||
|
@ -38,7 +81,7 @@ function setFilter(ev) {
|
||||||
os.popupMenu(items, ev.currentTarget ?? ev.target);
|
os.popupMenu(items, ev.currentTarget ?? ev.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
const headerActions = $computed(() => [{
|
const headerActions = $computed(() => tab === 'all' ? [{
|
||||||
text: i18n.ts.filter,
|
text: i18n.ts.filter,
|
||||||
icon: 'fas fa-filter',
|
icon: 'fas fa-filter',
|
||||||
highlighted: includeTypes != null,
|
highlighted: includeTypes != null,
|
||||||
|
@ -49,23 +92,10 @@ const headerActions = $computed(() => [{
|
||||||
handler: () => {
|
handler: () => {
|
||||||
os.apiWithDialog('notifications/mark-all-as-read');
|
os.apiWithDialog('notifications/mark-all-as-read');
|
||||||
},
|
},
|
||||||
}]);
|
}] : []);
|
||||||
|
|
||||||
const headerTabs = $computed(() => [{
|
|
||||||
key: 'all',
|
|
||||||
title: i18n.ts.all,
|
|
||||||
}, {
|
|
||||||
key: 'unread',
|
|
||||||
title: i18n.ts.unread,
|
|
||||||
}]);
|
|
||||||
|
|
||||||
definePageMetadata(computed(() => ({
|
definePageMetadata(computed(() => ({
|
||||||
title: i18n.ts.notifications,
|
title: i18n.ts.notifications,
|
||||||
icon: 'fas fa-bell',
|
icon: 'fas fa-bell',
|
||||||
})));
|
})));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.clupoqwt {
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<MkContainer>
|
<MkContainer>
|
||||||
<template #header><i class="fas fa-chart-bar" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template>
|
<template #header><i class="fas fa-chart-simple" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template>
|
||||||
<template #func>
|
<template #func>
|
||||||
<button class="_button" @click="showMenu">
|
<button class="_button" @click="showMenu">
|
||||||
<i class="fas fa-ellipsis-h"></i>
|
<i class="fas fa-ellipsis-h"></i>
|
||||||
|
|
|
@ -70,12 +70,6 @@ export const routes = [{
|
||||||
}, {
|
}, {
|
||||||
path: '/explore',
|
path: '/explore',
|
||||||
component: page(() => import('./pages/explore.vue')),
|
component: page(() => import('./pages/explore.vue')),
|
||||||
}, {
|
|
||||||
path: '/federation',
|
|
||||||
component: page(() => import('./pages/federation.vue')),
|
|
||||||
}, {
|
|
||||||
path: '/emojis',
|
|
||||||
component: page(() => import('./pages/emojis.vue')),
|
|
||||||
}, {
|
}, {
|
||||||
path: '/search',
|
path: '/search',
|
||||||
component: page(() => import('./pages/search.vue')),
|
component: page(() => import('./pages/search.vue')),
|
||||||
|
@ -167,17 +161,12 @@ export const routes = [{
|
||||||
}, {
|
}, {
|
||||||
path: '/my/notifications',
|
path: '/my/notifications',
|
||||||
component: page(() => import('./pages/notifications.vue')),
|
component: page(() => import('./pages/notifications.vue')),
|
||||||
|
hash: 'initialTab',
|
||||||
loginRequired: true,
|
loginRequired: true,
|
||||||
}, {
|
}, {
|
||||||
path: '/my/favorites',
|
path: '/my/favorites',
|
||||||
component: page(() => import('./pages/favorites.vue')),
|
component: page(() => import('./pages/favorites.vue')),
|
||||||
loginRequired: true,
|
loginRequired: true,
|
||||||
}, {
|
|
||||||
path: '/my/messages',
|
|
||||||
component: page(() => import('./pages/messages.vue')),
|
|
||||||
}, {
|
|
||||||
path: '/my/mentions',
|
|
||||||
component: page(() => import('./pages/mentions.vue')),
|
|
||||||
}, {
|
}, {
|
||||||
name: 'messaging',
|
name: 'messaging',
|
||||||
path: '/my/messaging',
|
path: '/my/messaging',
|
||||||
|
|
Loading…
Reference in a new issue