mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-05-22 22:14:06 +02:00
refactor(frontend): フロントエンドの型エラー解消(途中まで) (#16539)
* fix(frontend): FormLinkをボタンとして使用した際にエラーが出る問題を修正 * refactor(frontend): フロントエンドの型エラー解消 * remove unused ts-expect-error * migrate * remove unrelated changes * fix lint * more type fixes
This commit is contained in:
@@ -11,12 +11,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off">
|
||||
<template #prefix><i class="ti ti-search"></i></template>
|
||||
</MkInput>
|
||||
|
||||
<!-- たくさんあると邪魔
|
||||
<div class="tags">
|
||||
<span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<MkFoldableSection v-if="searchEmojis">
|
||||
@@ -42,51 +36,33 @@ import XEmoji from './emojis.emoji.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||
import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js';
|
||||
import { customEmojis, customEmojiCategories } from '@/custom-emojis.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { $i } from '@/i.js';
|
||||
|
||||
const customEmojiTags = getCustomEmojiTags();
|
||||
const q = ref('');
|
||||
const searchEmojis = ref<Misskey.entities.EmojiSimple[] | null>(null);
|
||||
const selectedTags = ref(new Set());
|
||||
|
||||
function search() {
|
||||
if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) {
|
||||
if (q.value === '' || q.value == null) {
|
||||
searchEmojis.value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedTags.value.size === 0) {
|
||||
const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
|
||||
const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g);
|
||||
|
||||
if (queryarry) {
|
||||
searchEmojis.value = customEmojis.value.filter(emoji =>
|
||||
queryarry.includes(`:${emoji.name}:`),
|
||||
);
|
||||
} else {
|
||||
searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
|
||||
}
|
||||
if (queryarry) {
|
||||
searchEmojis.value = customEmojis.value.filter(emoji =>
|
||||
queryarry.includes(`:${emoji.name}:`),
|
||||
);
|
||||
} else {
|
||||
searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q.value)) && [...selectedTags.value].every(t => emoji.aliases.includes(t)));
|
||||
}
|
||||
}
|
||||
|
||||
function toggleTag(tag) {
|
||||
if (selectedTags.value.has(tag)) {
|
||||
selectedTags.value.delete(tag);
|
||||
} else {
|
||||
selectedTags.value.add(tag);
|
||||
searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q.value));
|
||||
}
|
||||
}
|
||||
|
||||
watch(q, () => {
|
||||
search();
|
||||
});
|
||||
|
||||
watch(selectedTags, () => {
|
||||
search();
|
||||
}, { deep: true });
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
}, {
|
||||
label: `${i18n.ts.registeredAt} (${i18n.ts.ascendingOrder})`,
|
||||
value: '-firstRetrievedAt',
|
||||
}] as const"
|
||||
}]"
|
||||
>
|
||||
<template #label>{{ i18n.ts.sort }}</template>
|
||||
</MkSelect>
|
||||
|
||||
@@ -233,6 +233,7 @@ import { ensureSignin, iAmAdmin, iAmModerator } from '@/i.js';
|
||||
import MkRolePreview from '@/components/MkRolePreview.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import { Paginator } from '@/utility/paginator.js';
|
||||
import type { ChartSrc } from '@/components/MkChart.vue';
|
||||
|
||||
const $i = ensureSignin();
|
||||
|
||||
@@ -246,7 +247,7 @@ const props = withDefaults(defineProps<{
|
||||
const result = await _fetch_();
|
||||
|
||||
const tab = ref(props.initialTab);
|
||||
const chartSrc = ref('per-user-notes');
|
||||
const chartSrc = ref<ChartSrc>('per-user-notes');
|
||||
const user = ref(result.user);
|
||||
const info = ref(result.info);
|
||||
const ips = ref(result.ips);
|
||||
@@ -429,7 +430,7 @@ async function assignRole() {
|
||||
title: i18n.ts._role.chooseRoleToAssign,
|
||||
items: roles.map(r => ({ text: r.name, value: r.id })),
|
||||
});
|
||||
if (canceled) return;
|
||||
if (canceled || roleId == null) return;
|
||||
|
||||
const { canceled: canceled2, result: period } = await os.select({
|
||||
title: i18n.ts.period + ': ' + roles.find(r => r.id === roleId)!.name,
|
||||
|
||||
@@ -303,8 +303,8 @@ async function onFileSelectClicked() {
|
||||
const driveFiles = await chooseFileFromPcAndUpload({
|
||||
multiple: true,
|
||||
folderId: selectedFolderId.value,
|
||||
// 拡張子は消す
|
||||
nameConverter: (file) => file.name.replace(/\.[a-zA-Z0-9]+$/, ''),
|
||||
// // 拡張子は消す
|
||||
// nameConverter: (file) => file.name.replace(/\.[a-zA-Z0-9]+$/, ''),
|
||||
});
|
||||
|
||||
gridItems.value.push(...driveFiles.map(fromDriveFile));
|
||||
|
||||
@@ -26,10 +26,10 @@ const chartEl = useTemplateRef('chartEl');
|
||||
|
||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||
|
||||
let chartInstance: Chart;
|
||||
let chartInstance: Chart | null = null;
|
||||
|
||||
function setData(values) {
|
||||
if (chartInstance == null) return;
|
||||
if (chartInstance == null || chartInstance.data.labels == null) return;
|
||||
for (const value of values) {
|
||||
chartInstance.data.labels.push('');
|
||||
chartInstance.data.datasets[0].data.push(value);
|
||||
@@ -42,7 +42,7 @@ function setData(values) {
|
||||
}
|
||||
|
||||
function pushData(value) {
|
||||
if (chartInstance == null) return;
|
||||
if (chartInstance == null || chartInstance.data.labels == null) return;
|
||||
chartInstance.data.labels.push('');
|
||||
chartInstance.data.datasets[0].data.push(value);
|
||||
if (chartInstance.data.datasets[0].data.length > 200) {
|
||||
@@ -69,6 +69,8 @@ const color =
|
||||
onMounted(() => {
|
||||
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
|
||||
if (chartEl.value == null) return;
|
||||
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
type: 'line',
|
||||
data: {
|
||||
|
||||
@@ -210,6 +210,7 @@ async function fetchCurrentQueue() {
|
||||
}
|
||||
|
||||
async function fetchJobs() {
|
||||
if (tab.value === '-') return;
|
||||
jobsFetching.value = true;
|
||||
const state = jobState.value;
|
||||
jobs.value = await misskeyApi('admin/queue/jobs', {
|
||||
@@ -307,6 +308,7 @@ async function removeJobs() {
|
||||
}
|
||||
|
||||
async function refreshJob(jobId: string) {
|
||||
if (tab.value === '-') return;
|
||||
const newJob = await misskeyApi('admin/queue/show-job', { queue: tab.value, jobId });
|
||||
const index = jobs.value.findIndex((job) => job.id === jobId);
|
||||
if (index !== -1) {
|
||||
|
||||
@@ -26,7 +26,7 @@ initChart();
|
||||
|
||||
const chartEl = useTemplateRef('chartEl');
|
||||
const now = new Date();
|
||||
let chartInstance: Chart = null;
|
||||
let chartInstance: Chart | null = null;
|
||||
const chartLimit = 7;
|
||||
const fetching = ref(true);
|
||||
|
||||
|
||||
@@ -23,9 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="item _panel sub">
|
||||
<div class="icon"><i class="ti ti-world-download"></i></div>
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
<div v-if="federationSubActive != null" class="value">
|
||||
{{ number(federationSubActive) }}
|
||||
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationSubActiveDiff"></MkNumberDiff>
|
||||
<MkNumberDiff v-if="federationSubActiveDiff != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationSubActiveDiff"></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Sub</div>
|
||||
</div>
|
||||
@@ -33,9 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="item _panel pub">
|
||||
<div class="icon"><i class="ti ti-world-upload"></i></div>
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
<div v-if="federationPubActive != null" class="value">
|
||||
{{ number(federationPubActive) }}
|
||||
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationPubActiveDiff"></MkNumberDiff>
|
||||
<MkNumberDiff v-if="federationPubActiveDiff != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="federationPubActiveDiff"></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Pub</div>
|
||||
</div>
|
||||
|
||||
@@ -20,8 +20,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
import { ref } from 'vue';
|
||||
import MkHeatmap from '@/components/MkHeatmap.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import type { HeatmapSource } from '@/components/MkHeatmap.vue';
|
||||
|
||||
const src = ref('active-users');
|
||||
const src = ref<HeatmapSource>('active-users');
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
||||
@@ -32,15 +32,17 @@ const { handler: externalTooltipHandler } = useChartTooltip({
|
||||
position: 'middle',
|
||||
});
|
||||
|
||||
let chartInstance: Chart;
|
||||
let chartInstance: Chart | null = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (chartEl.value == null) return;
|
||||
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: props.data.map(x => x.name),
|
||||
datasets: [{
|
||||
backgroundColor: props.data.map(x => x.color),
|
||||
backgroundColor: props.data.map(x => x.color ?? '#000'),
|
||||
borderColor: getComputedStyle(window.document.documentElement).getPropertyValue('--MI_THEME-panel'),
|
||||
borderWidth: 2,
|
||||
hoverOffset: 0,
|
||||
@@ -57,9 +59,10 @@ onMounted(() => {
|
||||
},
|
||||
},
|
||||
onClick: (ev) => {
|
||||
const hit = chartInstance.getElementsAtEventForMode(ev, 'nearest', { intersect: true }, false)[0];
|
||||
if (hit && props.data[hit.index].onClick) {
|
||||
props.data[hit.index].onClick();
|
||||
if (ev.native == null) return;
|
||||
const hit = chartInstance!.getElementsAtEventForMode(ev.native, 'nearest', { intersect: true }, false)[0];
|
||||
if (hit && props.data[hit.index].onClick != null) {
|
||||
props.data[hit.index].onClick!();
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
|
||||
@@ -26,10 +26,10 @@ const chartEl = useTemplateRef('chartEl');
|
||||
|
||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||
|
||||
let chartInstance: Chart;
|
||||
let chartInstance: Chart | null = null;
|
||||
|
||||
function setData(values) {
|
||||
if (chartInstance == null) return;
|
||||
function setData(values: number[]) {
|
||||
if (chartInstance == null || chartInstance.data.labels == null) return;
|
||||
for (const value of values) {
|
||||
chartInstance.data.labels.push('');
|
||||
chartInstance.data.datasets[0].data.push(value);
|
||||
@@ -41,8 +41,8 @@ function setData(values) {
|
||||
chartInstance.update();
|
||||
}
|
||||
|
||||
function pushData(value) {
|
||||
if (chartInstance == null) return;
|
||||
function pushData(value: number) {
|
||||
if (chartInstance == null || chartInstance.data.labels == null) return;
|
||||
chartInstance.data.labels.push('');
|
||||
chartInstance.data.datasets[0].data.push(value);
|
||||
if (chartInstance.data.datasets[0].data.length > 100) {
|
||||
@@ -67,6 +67,8 @@ const color =
|
||||
'?' as never;
|
||||
|
||||
onMounted(() => {
|
||||
if (chartEl.value == null) return;
|
||||
|
||||
const vLineColor = store.s.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
|
||||
|
||||
chartInstance = new Chart(chartEl.value, {
|
||||
|
||||
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
import { markRaw, onMounted, onUnmounted, ref, useTemplateRef } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XChart from './overview.queue.chart.vue';
|
||||
import type { ApQueueDomain } from '@/pages/admin/queue.vue';
|
||||
import type { ApQueueDomain } from '@/pages/admin/federation-job-queue.vue';
|
||||
import number from '@/filters/number.js';
|
||||
import { useStream } from '@/stream.js';
|
||||
import { genId } from '@/utility/id.js';
|
||||
@@ -64,10 +64,10 @@ function onStats(stats: Misskey.entities.QueueStats) {
|
||||
delayed.value = stats[props.domain].delayed;
|
||||
waiting.value = stats[props.domain].waiting;
|
||||
|
||||
chartProcess.value.pushData(stats[props.domain].activeSincePrevTick);
|
||||
chartActive.value.pushData(stats[props.domain].active);
|
||||
chartDelayed.value.pushData(stats[props.domain].delayed);
|
||||
chartWaiting.value.pushData(stats[props.domain].waiting);
|
||||
chartProcess.value?.pushData(stats[props.domain].activeSincePrevTick);
|
||||
chartActive.value?.pushData(stats[props.domain].active);
|
||||
chartDelayed.value?.pushData(stats[props.domain].delayed);
|
||||
chartWaiting.value?.pushData(stats[props.domain].waiting);
|
||||
}
|
||||
|
||||
function onStatsLog(statsLog: Misskey.entities.QueueStatsLog) {
|
||||
@@ -83,10 +83,10 @@ function onStatsLog(statsLog: Misskey.entities.QueueStatsLog) {
|
||||
dataWaiting.push(stats[props.domain].waiting);
|
||||
}
|
||||
|
||||
chartProcess.value.setData(dataProcess);
|
||||
chartActive.value.setData(dataActive);
|
||||
chartDelayed.value.setData(dataDelayed);
|
||||
chartWaiting.value.setData(dataWaiting);
|
||||
chartProcess.value?.setData(dataProcess);
|
||||
chartActive.value?.setData(dataActive);
|
||||
chartDelayed.value?.setData(dataDelayed);
|
||||
chartWaiting.value?.setData(dataWaiting);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
@@ -7,13 +7,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div>
|
||||
<Transition :name="prefer.s.animation ? '_transition_zoom' : ''" mode="out-in">
|
||||
<MkLoading v-if="fetching"/>
|
||||
<div v-else :class="$style.root">
|
||||
<div v-else-if="stats != null" :class="$style.root">
|
||||
<div class="item _panel users">
|
||||
<div class="icon"><i class="ti ti-users"></i></div>
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
<MkNumber :value="stats.originalUsersCount" style="margin-right: 0.5em;"/>
|
||||
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"></MkNumberDiff>
|
||||
<MkNumberDiff v-if="usersComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="usersComparedToThePrevDay"></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Users</div>
|
||||
</div>
|
||||
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
<MkNumber :value="stats.originalNotesCount" style="margin-right: 0.5em;"/>
|
||||
<MkNumberDiff v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"></MkNumberDiff>
|
||||
<MkNumberDiff v-if="notesComparedToThePrevDay != null" v-tooltip="i18n.ts.dayOverDayChanges" class="diff" :value="notesComparedToThePrevDay"></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Notes</div>
|
||||
</div>
|
||||
@@ -56,6 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MkError v-else/>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
@@ -71,8 +72,8 @@ import { customEmojis } from '@/custom-emojis.js';
|
||||
import { prefer } from '@/preferences.js';
|
||||
|
||||
const stats = ref<Misskey.entities.StatsResponse | null>(null);
|
||||
const usersComparedToThePrevDay = ref<number>();
|
||||
const notesComparedToThePrevDay = ref<number>();
|
||||
const usersComparedToThePrevDay = ref<number | null>(null);
|
||||
const notesComparedToThePrevDay = ref<number | null>(null);
|
||||
const onlineUsersCount = ref(0);
|
||||
const fetching = ref(true);
|
||||
|
||||
@@ -85,11 +86,11 @@ onMounted(async () => {
|
||||
onlineUsersCount.value = _onlineUsersCount;
|
||||
|
||||
misskeyApiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
|
||||
usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1];
|
||||
usersComparedToThePrevDay.value = _stats.originalUsersCount - chart.local.total[1];
|
||||
});
|
||||
|
||||
misskeyApiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
|
||||
notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1];
|
||||
notesComparedToThePrevDay.value = _stats.originalNotesCount - chart.local.total[1];
|
||||
});
|
||||
|
||||
fetching.value = false;
|
||||
|
||||
@@ -95,7 +95,7 @@ const federationPubActiveDiff = ref<number | null>(null);
|
||||
const federationSubActive = ref<number | null>(null);
|
||||
const federationSubActiveDiff = ref<number | null>(null);
|
||||
const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
|
||||
const activeInstances = shallowRef<Misskey.entities.FederationInstance | null>(null);
|
||||
const activeInstances = shallowRef<Misskey.entities.FederationInstancesResponse | null>(null);
|
||||
const queueStatsConnection = markRaw(useStream().useChannel('queueStats'));
|
||||
const now = new Date();
|
||||
const filesPagination = {
|
||||
|
||||
@@ -830,7 +830,6 @@ import { watch, ref, computed } from 'vue';
|
||||
import { throttle } from 'throttle-debounce';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import RolesEditorFormula from './RolesEditorFormula.vue';
|
||||
import type { GetMkSelectValueTypesFromDef, MkSelectItem } from '@/components/MkSelect.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkColorInput from '@/components/MkColorInput.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
|
||||
@@ -71,7 +71,7 @@ import { Paginator } from '@/utility/paginator.js';
|
||||
const router = useRouter();
|
||||
|
||||
const props = defineProps<{
|
||||
id?: string;
|
||||
id: string;
|
||||
}>();
|
||||
|
||||
const usersPaginator = markRaw(new Paginator('admin/roles/users', {
|
||||
|
||||
@@ -346,6 +346,7 @@ import { definePage } from '@/page.js';
|
||||
import { instance, fetchInstance } from '@/instance.js';
|
||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||
import { useRouter } from '@/router.js';
|
||||
import { deepClone } from '@/utility/clone.js';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
|
||||
const router = useRouter();
|
||||
@@ -353,10 +354,7 @@ const baseRoleQ = ref('');
|
||||
|
||||
const roles = await misskeyApi('admin/roles/list');
|
||||
|
||||
const policies = reactive<Record<typeof Misskey.rolePolicies[number], any>>({});
|
||||
for (const ROLE_POLICY of Misskey.rolePolicies) {
|
||||
policies[ROLE_POLICY] = instance.policies[ROLE_POLICY];
|
||||
}
|
||||
const policies = reactive(deepClone(instance.policies));
|
||||
|
||||
const avatarDecorationLimit = computed({
|
||||
get: () => Math.min(16, Math.max(0, policies.avatarDecorationLimit)),
|
||||
@@ -376,6 +374,7 @@ function matchQuery(keywords: string[]): boolean {
|
||||
|
||||
async function updateBaseRole() {
|
||||
await os.apiWithDialog('admin/roles/update-default-policies', {
|
||||
//@ts-expect-error misskey-js側の型定義が不十分
|
||||
policies,
|
||||
});
|
||||
fetchInstance(true);
|
||||
|
||||
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="title">{{ post.title }}</div>
|
||||
<div class="description"><Mfm :text="post.description"/></div>
|
||||
<div class="description"><Mfm v-if="post.description != null" :text="post.description"/></div>
|
||||
<div class="info">
|
||||
<i class="ti ti-clock"></i> <MkTime :time="post.createdAt" mode="detail"/>
|
||||
</div>
|
||||
@@ -93,7 +93,7 @@ const error = ref<any>(null);
|
||||
const otherPostsPaginator = markRaw(new Paginator('users/gallery/posts', {
|
||||
limit: 6,
|
||||
computedParams: computed(() => ({
|
||||
userId: post.value.user.id,
|
||||
userId: post.value!.user.id,
|
||||
})),
|
||||
}));
|
||||
|
||||
@@ -109,33 +109,38 @@ function fetchPost() {
|
||||
}
|
||||
|
||||
function copyLink() {
|
||||
if (!post.value) return;
|
||||
copyToClipboard(`${url}/gallery/${post.value.id}`);
|
||||
}
|
||||
|
||||
function share() {
|
||||
if (!post.value) return;
|
||||
navigator.share({
|
||||
title: post.value.title,
|
||||
text: post.value.description,
|
||||
text: post.value.description ?? undefined,
|
||||
url: `${url}/gallery/${post.value.id}`,
|
||||
});
|
||||
}
|
||||
|
||||
function shareWithNote() {
|
||||
if (!post.value) return;
|
||||
os.post({
|
||||
initialText: `${post.value.title} ${url}/gallery/${post.value.id}`,
|
||||
});
|
||||
}
|
||||
|
||||
function like() {
|
||||
if (!post.value) return;
|
||||
os.apiWithDialog('gallery/posts/like', {
|
||||
postId: props.postId,
|
||||
}).then(() => {
|
||||
post.value.isLiked = true;
|
||||
post.value.likedCount++;
|
||||
post.value!.isLiked = true;
|
||||
post.value!.likedCount++;
|
||||
});
|
||||
}
|
||||
|
||||
async function unlike() {
|
||||
if (!post.value) return;
|
||||
const confirm = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.unlikeConfirm,
|
||||
@@ -144,8 +149,8 @@ async function unlike() {
|
||||
os.apiWithDialog('gallery/posts/unlike', {
|
||||
postId: props.postId,
|
||||
}).then(() => {
|
||||
post.value.isLiked = false;
|
||||
post.value.likedCount--;
|
||||
post.value!.isLiked = false;
|
||||
post.value!.likedCount--;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton>
|
||||
<MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount != null && list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton>
|
||||
<MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton>
|
||||
<MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton>
|
||||
</div>
|
||||
@@ -41,7 +41,7 @@ const props = defineProps<{
|
||||
listId: string;
|
||||
}>();
|
||||
|
||||
const list = ref<Misskey.entities.UserList | null>(null);
|
||||
const list = ref<Misskey.entities.UsersListsShowResponse | null>(null);
|
||||
const error = ref<unknown | null>(null);
|
||||
const users = ref<Misskey.entities.UserDetailed[]>([]);
|
||||
|
||||
@@ -51,8 +51,9 @@ function fetchList(): void {
|
||||
forPublic: true,
|
||||
}).then(_list => {
|
||||
list.value = _list;
|
||||
if (_list.userIds == null || _list.userIds.length === 0) return;
|
||||
misskeyApi('users/show', {
|
||||
userIds: list.value.userIds,
|
||||
userIds: _list.userIds,
|
||||
}).then(_users => {
|
||||
users.value = _users;
|
||||
});
|
||||
@@ -68,7 +69,7 @@ function like() {
|
||||
}).then(() => {
|
||||
if (list.value == null) return;
|
||||
list.value.isLiked = true;
|
||||
list.value.likedCount++;
|
||||
list.value.likedCount = (list.value.likedCount != null ? list.value.likedCount + 1 : 1);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -79,7 +80,7 @@ function unlike() {
|
||||
}).then(() => {
|
||||
if (list.value == null) return;
|
||||
list.value.isLiked = false;
|
||||
list.value.likedCount--;
|
||||
list.value.likedCount = (list.value.likedCount != null ? Math.max(0, list.value.likedCount - 1) : 0);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -88,7 +89,7 @@ async function create() {
|
||||
const { canceled, result: name } = await os.inputText({
|
||||
title: i18n.ts.enterListName,
|
||||
});
|
||||
if (canceled) return;
|
||||
if (canceled || name == null) return;
|
||||
await os.apiWithDialog('users/lists/create-from-public', { name: name, listId: list.value.id });
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'note' }): void;
|
||||
(ev: 'remove'): void;
|
||||
}>();
|
||||
|
||||
const id = ref(props.modelValue.note);
|
||||
|
||||
@@ -71,7 +71,7 @@ async function add() {
|
||||
title: i18n.ts._pages.chooseBlock,
|
||||
items: getPageBlockList(),
|
||||
});
|
||||
if (canceled) return;
|
||||
if (canceled || type == null) return;
|
||||
|
||||
const id = genId();
|
||||
children.value.push({ id, type });
|
||||
|
||||
@@ -27,6 +27,7 @@ const props = defineProps<{
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: Misskey.entities.PageBlock & { type: 'text' }): void;
|
||||
(ev: 'remove'): void;
|
||||
}>();
|
||||
|
||||
let autocomplete: Autocomplete;
|
||||
@@ -42,6 +43,7 @@ watch(text, () => {
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
if (inputEl.value == null) return;
|
||||
autocomplete = new Autocomplete(inputEl.value, text);
|
||||
});
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||
<div class="_spacer" style="--MI_SPACER-w: 700px;">
|
||||
<div class="jqqmcavi">
|
||||
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
|
||||
<MkButton v-if="pageId && author != null" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
|
||||
<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
|
||||
<MkButton v-if="pageId" inline class="button" @click="duplicate"><i class="ti ti-copy"></i> {{ i18n.ts.duplicate }}</MkButton>
|
||||
<MkButton v-if="pageId && !readonly" inline class="button" danger @click="del"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton>
|
||||
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkInput>
|
||||
|
||||
<MkInput v-model="name">
|
||||
<template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
|
||||
<template #prefix>{{ url }}/@{{ author?.username ?? '???' }}/pages/</template>
|
||||
<template #label>{{ i18n.ts._pages.url }}</template>
|
||||
</MkInput>
|
||||
|
||||
@@ -85,7 +85,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const tab = ref('settings');
|
||||
const author = ref($i);
|
||||
const author = ref<Misskey.entities.User | null>($i);
|
||||
const readonly = ref(false);
|
||||
const page = ref<Misskey.entities.Page | null>(null);
|
||||
const pageId = ref<string | null>(null);
|
||||
@@ -202,11 +202,10 @@ async function duplicate() {
|
||||
|
||||
async function add() {
|
||||
const { canceled, result: type } = await os.select({
|
||||
type: null,
|
||||
title: i18n.ts._pages.chooseBlock,
|
||||
items: getPageBlockList(),
|
||||
});
|
||||
if (canceled) return;
|
||||
if (canceled || type == null) return;
|
||||
|
||||
const id = genId();
|
||||
content.value.push({ id, type });
|
||||
|
||||
@@ -164,7 +164,7 @@ const $i = ensureSignin();
|
||||
|
||||
const props = defineProps<{
|
||||
game: Misskey.entities.ReversiGameDetailed;
|
||||
connection?: Misskey.ChannelConnection<Misskey.Channels['reversiGame']> | null;
|
||||
connection?: Misskey.IChannelConnection<Misskey.Channels['reversiGame']> | null;
|
||||
}>();
|
||||
|
||||
const showBoardLabels = ref<boolean>(false);
|
||||
|
||||
@@ -132,7 +132,7 @@ const mapCategories = Array.from(new Set(Object.values(Reversi.maps).map(x => x.
|
||||
|
||||
const props = defineProps<{
|
||||
game: Misskey.entities.ReversiGameDetailed;
|
||||
connection: Misskey.ChannelConnection<Misskey.Channels['reversiGame']>;
|
||||
connection: Misskey.IChannelConnection<Misskey.Channels['reversiGame']>;
|
||||
}>();
|
||||
|
||||
const shareWhenStart = defineModel<boolean>('shareWhenStart', { default: false });
|
||||
|
||||
@@ -33,7 +33,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const game = shallowRef<Misskey.entities.ReversiGameDetailed | null>(null);
|
||||
const connection = shallowRef<Misskey.ChannelConnection | null>(null);
|
||||
const connection = shallowRef<Misskey.IChannelConnection<Misskey.Channels['reversiGame']> | null>(null);
|
||||
const shareWhenStart = ref(false);
|
||||
|
||||
watch(() => props.gameId, () => {
|
||||
|
||||
@@ -196,6 +196,7 @@ async function addSecurityKey() {
|
||||
if (auth.canceled) return;
|
||||
|
||||
const registrationOptions = parseCreationOptionsFromJSON({
|
||||
// @ts-expect-error misskey-js側に型がない
|
||||
publicKey: await os.apiWithDialog('i/2fa/register-key', {
|
||||
password: auth.result.password,
|
||||
token: auth.result.token,
|
||||
@@ -226,6 +227,7 @@ async function addSecurityKey() {
|
||||
password: auth.result.password,
|
||||
token: auth.result.token,
|
||||
name: name.result,
|
||||
// @ts-expect-error misskey-js側に型がない
|
||||
credential: credential.toJSON(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label><SearchLabel>{{ i18n.ts._accountSettings.makeNotesFollowersOnlyBefore }}</SearchLabel></template>
|
||||
|
||||
<div class="_gaps_s">
|
||||
<MkSelect :modelValue="makeNotesFollowersOnlyBefore_type" @update:modelValue="makeNotesFollowersOnlyBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null">
|
||||
<MkSelect v-model="makeNotesFollowersOnlyBefore_type">
|
||||
<option :value="null">{{ i18n.ts.none }}</option>
|
||||
<option value="relative">{{ i18n.ts._accountSettings.notesHavePassedSpecifiedPeriod }}</option>
|
||||
<option value="absolute">{{ i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime }}</option>
|
||||
@@ -140,7 +140,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkInput>
|
||||
|
||||
<MkInput
|
||||
v-if="makeNotesFollowersOnlyBefore_type === 'absolute'"
|
||||
v-if="makeNotesFollowersOnlyBefore_type === 'absolute' && makeNotesFollowersOnlyBefore != null"
|
||||
:modelValue="formatDateTimeString(new Date(makeNotesFollowersOnlyBefore * 1000), 'yyyy-MM-dd')"
|
||||
type="date"
|
||||
:manualSave="true"
|
||||
@@ -161,6 +161,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<div class="_gaps_s">
|
||||
<MkSelect
|
||||
v-model="makeNotesHiddenBefore_type"
|
||||
:items="[{
|
||||
value: null,
|
||||
label: i18n.ts.none
|
||||
@@ -170,7 +171,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
}, {
|
||||
value: 'absolute',
|
||||
label: i18n.ts._accountSettings.notesOlderThanSpecifiedDateAndTime
|
||||
}] as const" :modelValue="makeNotesHiddenBefore_type" @update:modelValue="makeNotesHiddenBefore = $event === 'relative' ? -604800 : $event === 'absolute' ? Math.floor(Date.now() / 1000) : null"
|
||||
}]"
|
||||
>
|
||||
</MkSelect>
|
||||
|
||||
@@ -189,7 +190,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkInput>
|
||||
|
||||
<MkInput
|
||||
v-if="makeNotesHiddenBefore_type === 'absolute'"
|
||||
v-if="makeNotesHiddenBefore_type === 'absolute' && makeNotesHiddenBefore != null"
|
||||
:modelValue="formatDateTimeString(new Date(makeNotesHiddenBefore * 1000), 'yyyy-MM-dd')"
|
||||
type="date"
|
||||
:manualSave="true"
|
||||
@@ -217,7 +218,6 @@ import { ref, computed, watch } from 'vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import FormSection from '@/components/form/section.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { instance } from '@/instance.js';
|
||||
@@ -247,14 +247,25 @@ const followingVisibility = ref($i.followingVisibility);
|
||||
const followersVisibility = ref($i.followersVisibility);
|
||||
const chatScope = ref($i.chatScope);
|
||||
|
||||
const makeNotesFollowersOnlyBefore_type = computed(() => {
|
||||
if (makeNotesFollowersOnlyBefore.value == null) {
|
||||
return null;
|
||||
} else if (makeNotesFollowersOnlyBefore.value >= 0) {
|
||||
return 'absolute';
|
||||
} else {
|
||||
return 'relative';
|
||||
}
|
||||
const makeNotesFollowersOnlyBefore_type = computed({
|
||||
get: () => {
|
||||
if (makeNotesFollowersOnlyBefore.value == null) {
|
||||
return null;
|
||||
} else if (makeNotesFollowersOnlyBefore.value >= 0) {
|
||||
return 'absolute';
|
||||
} else {
|
||||
return 'relative';
|
||||
}
|
||||
},
|
||||
set(value) {
|
||||
if (value === 'relative') {
|
||||
makeNotesFollowersOnlyBefore.value = -604800;
|
||||
} else if (value === 'absolute') {
|
||||
makeNotesFollowersOnlyBefore.value = Math.floor(Date.now() / 1000);
|
||||
} else {
|
||||
makeNotesFollowersOnlyBefore.value = null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const makeNotesFollowersOnlyBefore_presets = [
|
||||
@@ -288,14 +299,25 @@ const makeNotesFollowersOnlyBefore_customMonths = computed({
|
||||
},
|
||||
});
|
||||
|
||||
const makeNotesHiddenBefore_type = computed(() => {
|
||||
if (makeNotesHiddenBefore.value == null) {
|
||||
return null;
|
||||
} else if (makeNotesHiddenBefore.value >= 0) {
|
||||
return 'absolute';
|
||||
} else {
|
||||
return 'relative';
|
||||
}
|
||||
const makeNotesHiddenBefore_type = computed({
|
||||
get: () => {
|
||||
if (makeNotesHiddenBefore.value == null) {
|
||||
return null;
|
||||
} else if (makeNotesHiddenBefore.value >= 0) {
|
||||
return 'absolute';
|
||||
} else {
|
||||
return 'relative';
|
||||
}
|
||||
},
|
||||
set(value) {
|
||||
if (value === 'relative') {
|
||||
makeNotesHiddenBefore.value = -604800;
|
||||
} else if (value === 'absolute') {
|
||||
makeNotesHiddenBefore.value = Math.floor(Date.now() / 1000);
|
||||
} else {
|
||||
makeNotesHiddenBefore.value = null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const makeNotesHiddenBefore_presets = [
|
||||
|
||||
@@ -41,25 +41,23 @@ import * as os from '@/os.js';
|
||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||
import { playMisskeySfxFile, soundsTypes, getSoundDuration } from '@/utility/sound.js';
|
||||
import { selectFile } from '@/utility/drive.js';
|
||||
import type { SoundStore } from '@/preferences/def.js';
|
||||
|
||||
const props = defineProps<{
|
||||
type: SoundType;
|
||||
fileId?: string;
|
||||
fileUrl?: string;
|
||||
volume: number;
|
||||
def: SoundStore;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update', result: { type: SoundType; fileId?: string; fileUrl?: string; volume: number; }): void;
|
||||
}>();
|
||||
|
||||
const type = ref<SoundType>(props.type);
|
||||
const fileId = ref(props.fileId);
|
||||
const fileUrl = ref(props.fileUrl);
|
||||
const type = ref<SoundType>(props.def.type);
|
||||
const fileId = ref('fileId' in props.def ? props.def.fileId : undefined);
|
||||
const fileUrl = ref('fileUrl' in props.def ? props.def.fileUrl : undefined);
|
||||
const fileName = ref<string>('');
|
||||
const driveFileError = ref(false);
|
||||
const hasChanged = ref(false);
|
||||
const volume = ref(props.volume);
|
||||
const volume = ref(props.def.volume);
|
||||
|
||||
if (type.value === '_driveFile_' && fileId.value) {
|
||||
await misskeyApi('drive/files/show', {
|
||||
|
||||
@@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #suffix>{{ getSoundTypeName(sounds[type].type) }}</template>
|
||||
<Suspense>
|
||||
<template #default>
|
||||
<XSound :type="sounds[type].type" :volume="sounds[type].volume" :fileId="sounds[type].fileId" :fileUrl="sounds[type].fileUrl" @update="(res) => updated(type, res)"/>
|
||||
<XSound :def="sounds[type]" @update="(res) => updated(type, res)"/>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<MkLoading/>
|
||||
|
||||
@@ -112,8 +112,7 @@ async function init() {
|
||||
...(visibleUserIds ? visibleUserIds.split(',').map(userId => ({ userId })) : []),
|
||||
...(visibleAccts ? visibleAccts.split(',').map(Misskey.acct.parse) : []),
|
||||
]
|
||||
// TypeScriptの指示通りに変換する
|
||||
.map(q => 'username' in q ? { username: q.username, host: q.host === null ? undefined : q.host } : q)
|
||||
// @ts-expect-error payloadの引数側の型が正常に解決されない
|
||||
.map(q => misskeyApi('users/show', q)
|
||||
.then(user => {
|
||||
visibleUsers.value.push(user);
|
||||
|
||||
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPagination v-slot="{items}" :paginator="paginator" withControl>
|
||||
<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/list/${ list.id }`">
|
||||
<div>{{ list.name }}</div>
|
||||
<MkAvatars :userIds="list.userIds"/>
|
||||
<MkAvatars v-if="list.userIds != null" :userIds="list.userIds"/>
|
||||
</MkA>
|
||||
</MkPagination>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user