diff options
| author | Marie <marie@kaifa.ch> | 2024-01-09 02:57:57 +0100 |
|---|---|---|
| committer | Marie <marie@kaifa.ch> | 2024-01-09 02:57:57 +0100 |
| commit | 7552cea69ae21b31799d54b246dcd45e96654926 (patch) | |
| tree | 389031f25fee72180157869a9e60c31704e198a9 /packages/frontend/src/components | |
| parent | merge: additional authorised fetch logging (#328) (diff) | |
| parent | feat(ci): api.jsonのバリデーションチェックCIを追加 (#12950) (diff) | |
| download | sharkey-7552cea69ae21b31799d54b246dcd45e96654926.tar.gz sharkey-7552cea69ae21b31799d54b246dcd45e96654926.tar.bz2 sharkey-7552cea69ae21b31799d54b246dcd45e96654926.zip | |
merge: upstream
Diffstat (limited to 'packages/frontend/src/components')
64 files changed, 397 insertions, 277 deletions
diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue index b11cf1c8a0..d0d67661fb 100644 --- a/packages/frontend/src/components/MkAccountMoved.vue +++ b/packages/frontend/src/components/MkAccountMoved.vue @@ -17,7 +17,7 @@ import * as Misskey from 'misskey-js'; import MkMention from './MkMention.vue'; import { i18n } from '@/i18n.js'; import { host as localHost } from '@/config.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const user = ref<Misskey.entities.UserLite>(); @@ -25,7 +25,7 @@ const props = defineProps<{ movedTo: string; // user id }>(); -api('users/show', { userId: props.movedTo }).then(u => user.value = u); +misskeyApi('users/show', { userId: props.movedTo }).then(u => user.value = u); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue index 333f62e29c..9a7078c2f9 100644 --- a/packages/frontend/src/components/MkAchievements.vue +++ b/packages/frontend/src/components/MkAchievements.vue @@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only import * as Misskey from 'misskey-js'; import { onMounted, ref, computed } from 'vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js'; @@ -71,7 +72,7 @@ const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x))); function fetch() { - os.api('users/achievements', { userId: props.user.id }).then(res => { + misskeyApi('users/achievements', { userId: props.user.id }).then(res => { achievements.value = []; for (const t of ACHIEVEMENT_TYPES) { const a = res.find(x => x.name === t); diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue index 4c6e3e693a..eca5daf1c1 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.vue +++ b/packages/frontend/src/components/MkAnnouncementDialog.vue @@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import MkModal from '@/components/MkModal.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; @@ -49,7 +50,7 @@ async function ok() { } modal.value.close(); - os.api('i/read-announcement', { announcementId: props.announcement.id }); + misskeyApi('i/read-announcement', { announcementId: props.announcement.id }); updateAccount({ unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id), }); diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 6fd42f1eea..9ec44a9561 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -45,6 +45,7 @@ import contains from '@/scripts/contains.js'; import { char2twemojiFilePath, char2fluentEmojiFilePath, char2tossfaceFilePath } from '@/scripts/emoji-base.js'; import { acct } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { emojilist, getEmojiName } from '@/scripts/emojilist.js'; import { i18n } from '@/i18n.js'; @@ -201,7 +202,7 @@ function exec() { users.value = JSON.parse(cache); fetching.value = false; } else { - os.api('users/search-by-username-and-host', { + misskeyApi('users/search-by-username-and-host', { username: props.q, limit: 10, detail: false, @@ -224,7 +225,7 @@ function exec() { hashtags.value = hashtags; fetching.value = false; } else { - os.api('hashtags/search', { + misskeyApi('hashtags/search', { query: props.q, limit: 30, }).then(searchedHashtags => { diff --git a/packages/frontend/src/components/MkAvatars.vue b/packages/frontend/src/components/MkAvatars.vue index 5644a324cf..6ef4a7dfe2 100644 --- a/packages/frontend/src/components/MkAvatars.vue +++ b/packages/frontend/src/components/MkAvatars.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = withDefaults(defineProps<{ userIds: string[]; @@ -27,7 +27,7 @@ const props = withDefaults(defineProps<{ const users = ref<Misskey.entities.UserLite[]>([]); onMounted(async () => { - users.value = await os.api('users/show', { + users.value = await misskeyApi('users/show', { userIds: props.userIds, }) as unknown as Misskey.entities.UserLite[]; }); diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 9fcc49d3f0..da13f8a067 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -131,6 +131,10 @@ function onMousedown(evt: MouseEvent): void { box-sizing: border-box; transition: background 0.1s ease; + &:hover { + text-decoration: none; + } + &:not(:disabled):hover { background: var(--buttonHoverBg); } diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue index 40bca11e64..f60c721eae 100644 --- a/packages/frontend/src/components/MkCaptcha.vue +++ b/packages/frontend/src/components/MkCaptcha.vue @@ -6,12 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> <span v-if="!available">{{ i18n.ts.waiting }}<MkEllipsis/></span> - <div ref="captchaEl"></div> + <div v-if="props.provider == 'mcaptcha'"> + <div id="mcaptcha__widget-container" class="m-captcha-style"></div> + <div ref="captchaEl"></div> + </div> + <div v-else ref="captchaEl"></div> </div> </template> <script lang="ts" setup> -import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from 'vue'; +import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch, onUnmounted } from 'vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -26,7 +30,7 @@ export type Captcha = { getResponse(id: string): string; }; -export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile'; +export type CaptchaProvider = 'hcaptcha' | 'recaptcha' | 'turnstile' | 'mcaptcha'; type CaptchaContainer = { readonly [_ in CaptchaProvider]?: Captcha; @@ -39,6 +43,7 @@ declare global { const props = defineProps<{ provider: CaptchaProvider; sitekey: string | null; // null will show error on request + instanceUrl?: string | null; modelValue?: string | null; }>(); @@ -55,6 +60,7 @@ const variable = computed(() => { case 'hcaptcha': return 'hcaptcha'; case 'recaptcha': return 'grecaptcha'; case 'turnstile': return 'turnstile'; + case 'mcaptcha': return 'mcaptcha'; } }); @@ -65,6 +71,7 @@ const src = computed(() => { case 'hcaptcha': return 'https://js.hcaptcha.com/1/api.js?render=explicit&recaptchacompat=off'; case 'recaptcha': return 'https://www.recaptcha.net/recaptcha/api.js?render=explicit'; case 'turnstile': return 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; + case 'mcaptcha': return null; } }); @@ -72,9 +79,9 @@ const scriptId = computed(() => `script-${props.provider}`); const captcha = computed<Captcha>(() => window[variable.value] || {} as unknown as Captcha); -if (loaded) { +if (loaded || props.provider === 'mcaptcha') { available.value = true; -} else { +} else if (src.value !== null) { (document.getElementById(scriptId.value) ?? document.head.appendChild(Object.assign(document.createElement('script'), { async: true, id: scriptId.value, @@ -87,7 +94,7 @@ function reset() { if (captcha.value.reset) captcha.value.reset(); } -function requestRender() { +async function requestRender() { if (captcha.value.render && captchaEl.value instanceof Element) { captcha.value.render(captchaEl.value, { sitekey: props.sitekey, @@ -96,6 +103,15 @@ function requestRender() { 'expired-callback': callback, 'error-callback': callback, }); + } else if (props.provider === 'mcaptcha' && props.instanceUrl && props.sitekey) { + const { default: Widget } = await import('@mcaptcha/vanilla-glue'); + // @ts-expect-error avoid typecheck error + new Widget({ + siteKey: { + instanceUrl: new URL(props.instanceUrl), + key: props.sitekey, + }, + }); } else { window.setTimeout(requestRender, 1); } @@ -105,14 +121,27 @@ function callback(response?: string) { emit('update:modelValue', typeof response === 'string' ? response : null); } +function onReceivedMessage(message: MessageEvent) { + if (message.data.token) { + if (props.instanceUrl && new URL(message.origin).host === new URL(props.instanceUrl).host) { + callback(<string>message.data.token); + } + } +} + onMounted(() => { if (available.value) { + window.addEventListener('message', onReceivedMessage); requestRender(); } else { watch(available, requestRender); } }); +onUnmounted(() => { + window.removeEventListener('message', onReceivedMessage); +}); + onBeforeUnmount(() => { reset(); }); diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index 4a58204b5b..393699da6f 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = withDefaults(defineProps<{ @@ -44,12 +44,12 @@ async function onClick() { try { if (isFollowing.value) { - await os.api('channels/unfollow', { + await misskeyApi('channels/unfollow', { channelId: props.channel.id, }); isFollowing.value = false; } else { - await os.api('channels/follow', { + await misskeyApi('channels/follow', { channelId: props.channel.id, }); isFollowing.value = true; diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue index adb3c134ae..82605123c5 100644 --- a/packages/frontend/src/components/MkChart.vue +++ b/packages/frontend/src/components/MkChart.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { onMounted, ref, shallowRef, watch, PropType } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -277,7 +277,7 @@ const exportData = () => { }; const fetchFederationChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/federation', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/federation', { limit: props.limit, span: props.span }); return { series: [{ name: 'Received', @@ -327,7 +327,7 @@ const fetchFederationChart = async (): Promise<typeof chartData> => { }; const fetchApRequestChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/ap-request', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/ap-request', { limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -349,7 +349,7 @@ const fetchApRequestChart = async (): Promise<typeof chartData> => { }; const fetchNotesChart = async (type: string): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -396,7 +396,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => { }; const fetchNotesTotalChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -415,7 +415,7 @@ const fetchNotesTotalChart = async (): Promise<typeof chartData> => { }; const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/users', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Combined', @@ -443,7 +443,7 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => { }; const fetchActiveUsersChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/active-users', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/active-users', { limit: props.limit, span: props.span }); return { series: [{ name: 'Read & Write', @@ -495,7 +495,7 @@ const fetchActiveUsersChart = async (): Promise<typeof chartData> => { }; const fetchDriveChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -531,7 +531,7 @@ const fetchDriveChart = async (): Promise<typeof chartData> => { }; const fetchDriveFilesChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span }); return { series: [{ name: 'All', @@ -566,7 +566,7 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => { }; const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'In', @@ -588,7 +588,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => { }; const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Users', @@ -603,7 +603,7 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData }; const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Notes', @@ -618,7 +618,7 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData }; const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Following', @@ -641,7 +641,7 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> = }; const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { bytes: true, series: [{ @@ -657,7 +657,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char }; const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/instance', { host: props.args.host, limit: props.limit, span: props.span }); return { series: [{ name: 'Drive files', @@ -672,7 +672,7 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char }; const fetchPerUserNotesChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/notes', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [...(props.args.withoutAll ? [] : [{ name: 'All', @@ -704,7 +704,7 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => { }; const fetchPerUserPvChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/pv', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/pv', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Unique PV (user)', @@ -731,7 +731,7 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => { }; const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -746,7 +746,7 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => { }; const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/following', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Local', @@ -761,7 +761,7 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => { }; const fetchPerUserDriveChart = async (): Promise<typeof chartData> => { - const raw = await os.apiGet('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); + const raw = await misskeyApiGet('charts/user/drive', { userId: props.args.user.id, limit: props.limit, span: props.span }); return { series: [{ name: 'Inc', diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue index 0ec69a69af..789acbd4c1 100644 --- a/packages/frontend/src/components/MkCodeEditor.vue +++ b/packages/frontend/src/components/MkCodeEditor.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.codeEditorScroller"> <textarea ref="inputEl" - v-model="vModel" + v-model="v" :class="[$style.textarea]" :disabled="disabled" :required="required" @@ -58,7 +58,6 @@ const emit = defineEmits<{ }>(); const { modelValue } = toRefs(props); -const vModel = ref<string>(modelValue.value ?? ''); const v = ref<string>(modelValue.value ?? ''); const focused = ref(false); const changed = ref(false); @@ -79,15 +78,14 @@ const onKeydown = (ev: KeyboardEvent) => { if (ev.code === 'Enter') { const pos = inputEl.value?.selectionStart ?? 0; - const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; + const posEnd = inputEl.value?.selectionEnd ?? v.value.length; if (pos === posEnd) { - const lines = vModel.value.slice(0, pos).split('\n'); + const lines = v.value.slice(0, pos).split('\n'); const currentLine = lines[lines.length - 1]; const currentLineSpaces = currentLine.match(/^\s+/); const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0; ev.preventDefault(); - vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos); - v.value = vModel.value; + v.value = v.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + v.value.slice(pos); nextTick(() => { inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta); }); @@ -97,9 +95,8 @@ const onKeydown = (ev: KeyboardEvent) => { if (ev.key === 'Tab') { const pos = inputEl.value?.selectionStart ?? 0; - const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length; - vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd); - v.value = vModel.value; + const posEnd = inputEl.value?.selectionEnd ?? v.value.length; + v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd); nextTick(() => { inputEl.value?.setSelectionRange(pos + 1, pos + 1); }); diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index 9969c10258..7d9b0c603f 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -49,9 +49,9 @@ import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; -import { useRouter } from '@/router.js'; import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js'; import { deviceKind } from '@/scripts/device-kind.js'; +import { useRouter } from '@/global/router/supplier.js'; const router = useRouter(); diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index dcaaa72cf4..a2c8fd4fa5 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, defineAsyncComponent, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { claimAchievement } from '@/scripts/achievements.js'; @@ -144,7 +145,7 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: props.folder.id, }); @@ -160,7 +161,7 @@ function onDrop(ev: DragEvent) { if (folder.id === props.folder.id) return; emit('removeFolder', folder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folder.id, parentId: props.folder.id, }).then(() => { @@ -214,7 +215,7 @@ function rename() { default: props.folder.name, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: props.folder.id, name: name, }); @@ -222,7 +223,7 @@ function rename() { } function deleteFolder() { - os.api('drive/folders/delete', { + misskeyApi('drive/folders/delete', { folderId: props.folder.id, }).then(() => { if (defaultStore.state.uploadFolder === props.folder.id) { diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index cac3c17c85..e8c22f5d31 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ @@ -112,7 +112,7 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: props.folder ? props.folder.id : null, }); @@ -126,7 +126,7 @@ function onDrop(ev: DragEvent) { // 移動先が自分自身ならreject if (props.folder && folder.id === props.folder.id) return; emit('removeFolder', folder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folder.id, parentId: props.folder ? props.folder.id : null, }); diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 00bb0e6e2b..2fa5c1a0f8 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -102,6 +102,7 @@ import XNavFolder from '@/components/MkDrive.navFolder.vue'; import XFolder from '@/components/MkDrive.folder.vue'; import XFile from '@/components/MkDrive.file.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -254,7 +255,7 @@ function onDrop(ev: DragEvent): any { const file = JSON.parse(driveFile); if (files.value.some(f => f.id === file.id)) return; removeFile(file.id); - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, folderId: folder.value ? folder.value.id : null, }); @@ -270,7 +271,7 @@ function onDrop(ev: DragEvent): any { if (folder.value && droppedFolder.id === folder.value.id) return false; if (folders.value.some(f => f.id === droppedFolder.id)) return false; removeFolder(droppedFolder.id); - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: droppedFolder.id, parentId: folder.value ? folder.value.id : null, }).then(() => { @@ -307,7 +308,7 @@ function urlUpload() { placeholder: i18n.ts.uploadFromUrlDescription, }).then(({ canceled, result: url }) => { if (canceled || !url) return; - os.api('drive/files/upload-from-url', { + misskeyApi('drive/files/upload-from-url', { url: url, folderId: folder.value ? folder.value.id : undefined, }); @@ -325,7 +326,7 @@ function createFolder() { placeholder: i18n.ts.folderName, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/create', { + misskeyApi('drive/folders/create', { name: name, parentId: folder.value ? folder.value.id : undefined, }).then(createdFolder => { @@ -341,7 +342,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { default: folderToRename.name, }).then(({ canceled, result: name }) => { if (canceled) return; - os.api('drive/folders/update', { + misskeyApi('drive/folders/update', { folderId: folderToRename.id, name: name, }).then(updatedFolder => { @@ -352,7 +353,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) { } function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) { - os.api('drive/folders/delete', { + misskeyApi('drive/folders/delete', { folderId: folderToDelete.id, }).then(() => { // 削除時に親フォルダに移動 @@ -436,7 +437,7 @@ function move(target?: Misskey.entities.DriveFolder) { fetching.value = true; - os.api('drive/folders/show', { + misskeyApi('drive/folders/show', { folderId: target, }).then(folderToMove => { folder.value = folderToMove; @@ -535,7 +536,7 @@ async function fetch() { const foldersMax = 30; const filesMax = 30; - const foldersPromise = os.api('drive/folders', { + const foldersPromise = misskeyApi('drive/folders', { folderId: folder.value ? folder.value.id : null, limit: foldersMax + 1, }).then(fetchedFolders => { @@ -546,7 +547,7 @@ async function fetch() { return fetchedFolders; }); - const filesPromise = os.api('drive/files', { + const filesPromise = misskeyApi('drive/files', { folderId: folder.value ? folder.value.id : null, type: props.type, limit: filesMax + 1, @@ -571,7 +572,7 @@ function fetchMoreFolders() { const max = 30; - os.api('drive/folders', { + misskeyApi('drive/folders', { folderId: folder.value ? folder.value.id : null, type: props.type, untilId: folders.value.at(-1)?.id, @@ -594,7 +595,7 @@ function fetchMoreFiles() { const max = 30; // ファイル一覧取得 - os.api('drive/files', { + misskeyApi('drive/files', { folderId: folder.value ? folder.value.id : null, type: props.type, untilId: files.value.at(-1)?.id, diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue index 6d1bad7433..8a23d7d4bf 100644 --- a/packages/frontend/src/components/MkFeaturedPhotos.vue +++ b/packages/frontend/src/components/MkFeaturedPhotos.vue @@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const meta = ref<Misskey.entities.MetaResponse>(); -os.api('meta', { detail: true }).then(gotMeta => { +misskeyApi('meta', { detail: true }).then(gotMeta => { meta.value = gotMeta; }); </script> diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index d1b1956a03..1aae460117 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -38,11 +38,12 @@ SPDX-License-Identifier: AGPL-3.0-only import { onBeforeUnmount, onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { $i } from '@/account.js'; -import { defaultStore } from "@/store.js"; +import { defaultStore } from '@/store.js'; const props = withDefaults(defineProps<{ user: Misskey.entities.UserDetailed, @@ -63,7 +64,7 @@ const wait = ref(false); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { - os.api('users/show', { + misskeyApi('users/show', { userId: props.user.id, }) .then(onFollowChange); @@ -88,17 +89,17 @@ async function onClick() { if (canceled) return; - await os.api('following/delete', { + await misskeyApi('following/delete', { userId: props.user.id, }); } else { if (hasPendingFollowRequestFromYou.value) { - await os.api('following/requests/cancel', { + await misskeyApi('following/requests/cancel', { userId: props.user.id, }); hasPendingFollowRequestFromYou.value = false; } else { - await os.api('following/create', { + await misskeyApi('following/create', { userId: props.user.id, withReplies: defaultStore.state.defaultWithReplies, }); diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 6f882cfab7..2095a1dcea 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <MkSpacer :marginMin="20" :marginMax="32"> - <div class="_gaps_m"> + <div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m"> <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> <MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1"> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template> @@ -55,6 +55,10 @@ SPDX-License-Identifier: AGPL-3.0-only </MkButton> </template> </div> + <div v-else class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> </MkSpacer> </MkModalWindow> </template> @@ -70,6 +74,7 @@ import MkButton from './MkButton.vue'; import MkRadios from './MkRadios.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; +import { infoImageUrl } from '@/instance.js'; const props = defineProps<{ title: string; diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue index a57e6c9292..f47b680f83 100644 --- a/packages/frontend/src/components/MkHeatmap.vue +++ b/packages/frontend/src/components/MkHeatmap.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, nextTick, watch, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { alpha } from '@/scripts/color.js'; @@ -72,19 +72,19 @@ async function renderChart() { let values; if (props.src === 'active-users') { - const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' }); values = raw.readWrite; } else if (props.src === 'notes') { - const raw = await os.api('charts/notes', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' }); values = raw.local.inc; } else if (props.src === 'ap-requests-inbox-received') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.inboxReceived; } else if (props.src === 'ap-requests-deliver-succeeded') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.deliverSucceeded; } else if (props.src === 'ap-requests-deliver-failed') { - const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' }); values = raw.deliverFailed; } diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index 9cde197e19..a188e71174 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; const props = defineProps<{ @@ -27,7 +27,7 @@ const props = defineProps<{ const chartValues = ref<number[] | null>(null); -os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { +misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く res['requests.received'].splice(0, 1); chartValues.value = res['requests.received']; diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue index 7b763ad385..1576089657 100644 --- a/packages/frontend/src/components/MkInstanceStats.vue +++ b/packages/frontend/src/components/MkInstanceStats.vue @@ -90,6 +90,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkChart from '@/components/MkChart.vue'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkHeatmap from '@/components/MkHeatmap.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; @@ -162,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) { } onMounted(() => { - os.apiGet('federation/stats', { limit: 30 }).then(fedStats => { + misskeyApiGet('federation/stats', { limit: 30 }).then(fedStats => { createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({ name: x.host, color: x.themeColor, diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 870816b839..7870e1e4b8 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -190,6 +190,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; import * as sound from '@/scripts/sound.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; @@ -207,7 +208,7 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/global/router/supplier.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -319,7 +320,7 @@ const keymap = { }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -340,7 +341,7 @@ if (props.mock) { if (!props.mock) { useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -358,7 +359,7 @@ if (!props.mock) { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -377,7 +378,7 @@ if (!props.mock) { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -452,7 +453,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -478,7 +479,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { localOnly: visibility === 'local' ? true : localOnlySetting, visibility: noteVisibility, renoteId: appearNote.value.id, @@ -502,7 +503,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -524,7 +525,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -566,7 +567,7 @@ function like(): void { if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -589,7 +590,7 @@ function react(viaKeyboard = false): void { return; } - os.api('notes/like', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -610,7 +611,7 @@ function react(viaKeyboard = false): void { return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -632,7 +633,7 @@ function undoReact(note): void { return; } - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } @@ -641,7 +642,7 @@ function undoRenote(note) : void { if (props.mock) { return; } - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: note.id, }); os.toast(i18n.ts.rmboost); @@ -718,7 +719,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -776,7 +777,7 @@ function focusAfter() { } function readPromo() { - os.api('promo/read', { + misskeyApi('promo/read', { noteId: appearNote.value.id, }); isDeleted.value = true; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 537a99dc01..317666a5e2 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -237,6 +237,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -327,7 +328,7 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -346,7 +347,7 @@ const keymap = { }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -394,7 +395,7 @@ useNoteCapture({ }); useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -412,7 +413,7 @@ useTooltip(renoteButton, async (showing) => { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -493,7 +494,7 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -517,7 +518,7 @@ function renote(visibility: Visibility | 'local') { noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); } - os.api('notes/create', { + misskeyApi('notes/create', { localOnly: visibility === 'local' ? true : localOnlySetting, visibility: noteVisibility, renoteId: appearNote.value.id, @@ -537,7 +538,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -559,7 +560,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -596,7 +597,7 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -612,7 +613,7 @@ function react(viaKeyboard = false): void { reactionPicker.show(reactButton.value, reaction => { sound.play('reaction'); - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -629,7 +630,7 @@ function like(): void { pleaseLogin(); showMovedDialog(); sound.play('reaction'); - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -645,14 +646,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -712,7 +713,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -734,7 +735,7 @@ const repliesLoaded = ref(false); function loadReplies() { repliesLoaded.value = true; - os.api('notes/children', { + misskeyApi('notes/children', { noteId: appearNote.value.id, limit: 30, showQuotes: false, @@ -749,7 +750,7 @@ const quotesLoaded = ref(false); function loadQuotes() { quotesLoaded.value = true; - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 30, quote: true, @@ -764,7 +765,7 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; - os.api('notes/conversation', { + misskeyApi('notes/conversation', { noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index d725ca15af..9d403bf09e 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -92,6 +92,7 @@ import MkCwButton from '@/components/MkCwButton.vue'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; import * as sound from '@/scripts/sound.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { userPage } from '@/filters/user.js'; @@ -166,7 +167,7 @@ useNoteCapture({ }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -196,7 +197,7 @@ function react(viaKeyboard = false): void { showMovedDialog(); sound.play('reaction'); if (props.note.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -210,7 +211,7 @@ function react(viaKeyboard = false): void { } else { blur(); reactionPicker.show(reactButton.value, reaction => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: reaction, }); @@ -227,7 +228,7 @@ function like(): void { pleaseLogin(); showMovedDialog(); sound.play('reaction'); - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -243,14 +244,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -324,7 +325,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: props.note.id, channelId: props.note.channelId, }).then(() => { @@ -340,7 +341,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: props.note.id, localOnly: visibility === 'local' ? true : false, visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility, @@ -360,7 +361,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -382,7 +383,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -411,7 +412,7 @@ function menu(viaKeyboard = false): void { } if (props.detail) { - os.api('notes/children', { + misskeyApi('notes/children', { noteId: props.note.id, limit: numberOfReplies.value, showQuotes: false, diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ed79ca0d86..455a7c9429 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -145,7 +145,7 @@ import { getNoteSummary } from '@/scripts/get-note-summary.js'; import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; import { i18n } from '@/i18n.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { $i } from '@/account.js'; import { infoImageUrl } from '@/instance.js'; @@ -162,12 +162,12 @@ const followRequestDone = ref(false); const acceptFollowRequest = () => { followRequestDone.value = true; - os.api('following/requests/accept', { userId: props.notification.user.id }); + misskeyApi('following/requests/accept', { userId: props.notification.user.id }); }; const rejectFollowRequest = () => { followRequestDone.value = true; - os.api('following/requests/reject', { userId: props.notification.user.id }); + misskeyApi('following/requests/reject', { userId: props.notification.user.id }); }; </script> diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue index aa04ab253b..1ba4d713b0 100644 --- a/packages/frontend/src/components/MkNumber.vue +++ b/packages/frontend/src/components/MkNumber.vue @@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { reactive, watch } from 'vue'; -import gsap from 'gsap'; import number from '@/filters/number.js'; const props = defineProps<{ @@ -20,8 +19,24 @@ const tweened = reactive({ number: 0, }); -watch(() => props.value, (n) => { - gsap.to(tweened, { duration: 1, number: Number(n) || 0 }); +watch(() => props.value, (to, from) => { + // requestAnimationFrameを利用して、500msでfromからtoまでを1次関数的に変化させる + let start: number | null = null; + + function step(timestamp: number) { + if (start === null) { + start = timestamp; + } + const elapsed = timestamp - start; + tweened.number = (from ?? 0) + (to - (from ?? 0)) * elapsed / 500; + if (elapsed < 500) { + window.requestAnimationFrame(step); + } else { + tweened.number = to; + } + } + + window.requestAnimationFrame(step); }, { immediate: true, }); diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue index 13a703e9f6..2696baa0d6 100644 --- a/packages/frontend/src/components/MkPageWindow.vue +++ b/packages/frontend/src/components/MkPageWindow.vue @@ -23,26 +23,26 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <div ref="contents" :class="$style.root" style="container-type: inline-size;"> - <RouterView :key="reloadCount" :router="router"/> + <RouterView :key="reloadCount" :router="windowRouter"/> </div> </MkWindow> </template> <script lang="ts" setup> -import { ComputedRef, onMounted, onUnmounted, provide, shallowRef, ref, computed } from 'vue'; +import { computed, ComputedRef, onMounted, onUnmounted, provide, ref, shallowRef } from 'vue'; import RouterView from '@/components/global/RouterView.vue'; import MkWindow from '@/components/MkWindow.vue'; import { popout as _popout } from '@/scripts/popout.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; -import { mainRouter, routes, page } from '@/router.js'; -import { $i } from '@/account.js'; -import { Router, useScrollPositionManager } from '@/nirax.js'; +import { useScrollPositionManager } from '@/nirax.js'; import { i18n } from '@/i18n.js'; import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; import { openingWindowsCount } from '@/os.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { getScrollContainer } from '@/scripts/scroll.js'; +import { useRouterFactory } from '@/global/router/supplier.js'; +import { mainRouter } from '@/global/router/main.js'; const props = defineProps<{ initialPath: string; @@ -52,14 +52,15 @@ defineEmits<{ (ev: 'closed'): void; }>(); -const router = new Router(routes, props.initialPath, !!$i, page(() => import('@/pages/not-found.vue'))); +const routerFactory = useRouterFactory(); +const windowRouter = routerFactory(props.initialPath); const contents = shallowRef<HTMLElement>(); const pageMetadata = ref<null | ComputedRef<PageMetadata>>(); const windowEl = shallowRef<InstanceType<typeof MkWindow>>(); const history = ref<{ path: string; key: any; }[]>([{ - path: router.getCurrentPath(), - key: router.getCurrentKey(), + path: windowRouter.getCurrentPath(), + key: windowRouter.getCurrentKey(), }]); const buttonsLeft = computed(() => { const buttons = []; @@ -88,11 +89,11 @@ const buttonsRight = computed(() => { }); const reloadCount = ref(0); -router.addListener('push', ctx => { +windowRouter.addListener('push', ctx => { history.value.push({ path: ctx.path, key: ctx.key }); }); -provide('router', router); +provide('router', windowRouter); provideMetadataReceiver((info) => { pageMetadata.value = info; }); @@ -113,20 +114,20 @@ const contextmenu = computed(() => ([{ icon: 'ph-arrow-square-out ph-bold ph-lg', text: i18n.ts.openInNewTab, action: () => { - window.open(url + router.getCurrentPath(), '_blank', 'noopener'); + window.open(url + windowRouter.getCurrentPath(), '_blank', 'noopener'); windowEl.value.close(); }, }, { icon: 'ph-link ph-bold ph-lg', text: i18n.ts.copyLink, action: () => { - copyToClipboard(url + router.getCurrentPath()); + copyToClipboard(url + windowRouter.getCurrentPath()); }, }])); function back() { history.value.pop(); - router.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); + windowRouter.replace(history.value.at(-1)!.path, history.value.at(-1)!.key); } function reload() { @@ -138,16 +139,16 @@ function close() { } function expand() { - mainRouter.push(router.getCurrentPath(), 'forcePage'); + mainRouter.push(windowRouter.getCurrentPath(), 'forcePage'); windowEl.value.close(); } function popout() { - _popout(router.getCurrentPath(), windowEl.value.$el); + _popout(windowRouter.getCurrentPath(), windowEl.value.$el); windowEl.value.close(); } -useScrollPositionManager(() => getScrollContainer(contents.value), router); +useScrollPositionManager(() => getScrollContainer(contents.value), windowRouter); onMounted(() => { openingWindowsCount.value++; diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index bdd96238d3..f5b238046a 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -46,6 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js'; import { useDocumentVisibility } from '@/scripts/use-document-visibility.js'; import { defaultStore } from '@/store.js'; @@ -203,7 +204,7 @@ async function init(): Promise<void> { queue.value = new Map(); fetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi(props.pagination.endpoint, { ...params, limit: props.pagination.limit ?? 10, allowPartial: true, @@ -239,7 +240,7 @@ const fetchMore = async (): Promise<void> => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; moreFetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi(props.pagination.endpoint, { ...params, limit: SECOND_FETCH_LIMIT, ...(props.pagination.offsetMode ? { @@ -303,7 +304,7 @@ const fetchMoreAhead = async (): Promise<void> => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; moreFetching.value = true; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await os.api(props.pagination.endpoint, { + await misskeyApi(props.pagination.endpoint, { ...params, limit: SECOND_FETCH_LIMIT, ...(props.pagination.offsetMode ? { diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue index 85dd402730..8fbb0debb5 100644 --- a/packages/frontend/src/components/MkPasswordDialog.vue +++ b/packages/frontend/src/components/MkPasswordDialog.vue @@ -41,7 +41,9 @@ import MkInput from '@/components/MkInput.vue'; import MkButton from '@/components/MkButton.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; +import { signinRequired } from '@/account.js'; + +const $i = signinRequired(); const emit = defineEmits<{ (ev: 'done', v: { password: string; token: string | null; }): void; diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue index a741a3f7a8..e5e5a9edf4 100644 --- a/packages/frontend/src/components/MkPlusOneEffect.vue +++ b/packages/frontend/src/components/MkPlusOneEffect.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }"> - <span class="text" :class="{ up }">+1</span> + <span class="text" :class="{ up }">+{{ value }}</span> </div> </template> @@ -16,7 +16,9 @@ import * as os from '@/os.js'; const props = withDefaults(defineProps<{ x: number; y: number; + value?: number; }>(), { + value: 1, }); const emit = defineEmits<{ @@ -40,6 +42,7 @@ onMounted(() => { <style lang="scss" module> .root { + user-select: none; pointer-events: none; position: fixed; width: 128px; diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue index 6ee0c44658..1322cfdcb6 100644 --- a/packages/frontend/src/components/MkPoll.vue +++ b/packages/frontend/src/components/MkPoll.vue @@ -35,11 +35,13 @@ import * as Misskey from 'misskey-js'; import { sum } from '@/scripts/array.js'; import { pleaseLogin } from '@/scripts/please-login.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { useInterval } from '@/scripts/use-interval.js'; +import { WithNonNullable } from '@/type.js'; const props = defineProps<{ - note: Misskey.entities.Note; + note: WithNonNullable<Misskey.entities.Note, 'poll'>; readOnly?: boolean; }>(); @@ -94,7 +96,7 @@ const vote = async (id) => { if (canceled) return; } - await os.api('notes/polls/vote', { + await misskeyApi('notes/polls/vote', { noteId: props.note.id, choice: id, }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index aa37cef6c2..d76a5e1e04 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -116,12 +116,13 @@ import { extractMentions } from '@/scripts/extract-mentions.js'; import { formatTimeString } from '@/scripts/format-time-string.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFiles } from '@/scripts/select-file.js'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; -import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; +import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { uploadFile } from '@/scripts/upload.js'; import { deepClone } from '@/scripts/clone.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; @@ -130,6 +131,8 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; +const $i = signinRequired(); + const modal = inject('modal'); const props = withDefaults(defineProps<{ @@ -309,7 +312,7 @@ if (props.reply && props.reply.text != null) { } } -if ($i?.isSilenced && visibility.value === 'public') { +if ($i.isSilenced && visibility.value === 'public') { visibility.value = 'home'; } @@ -330,7 +333,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib if (visibility.value === 'specified') { if (props.reply.visibleUserIds) { - os.api('users/show', { + misskeyApi('users/show', { userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), }).then(users => { users.forEach(pushVisibleUser); @@ -338,7 +341,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib } if (props.reply.userId !== $i.id) { - os.api('users/show', { userId: props.reply.userId }).then(user => { + misskeyApi('users/show', { userId: props.reply.userId }).then(user => { pushVisibleUser(user); }); } @@ -389,7 +392,7 @@ function addMissingMention() { for (const x of extractMentions(ast)) { if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { - os.api('users/show', { username: x.username, host: x.host }).then(user => { + misskeyApi('users/show', { username: x.username, host: x.host }).then(user => { visibleUsers.value.push(user); }); } @@ -466,7 +469,7 @@ function setVisibility() { os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { currentVisibility: visibility.value, - isSilenced: $i?.isSilenced, + isSilenced: $i.isSilenced, localOnly: localOnly.value, src: visibilityButton.value, }, { @@ -759,7 +762,17 @@ async function post(ev?: MouseEvent) { if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); - postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; + if (!postData.text) { + postData.text = hashtags_; + } else { + const postTextLines = postData.text.split('\n'); + if (postTextLines[postTextLines.length - 1].trim() === '') { + postTextLines[postTextLines.length - 1] += hashtags_; + } else { + postTextLines[postTextLines.length - 1] += ' ' + hashtags_; + } + postData.text = postTextLines.join('\n'); + } } // plugin @@ -781,7 +794,7 @@ async function post(ev?: MouseEvent) { } posting.value = true; - os.api(postData.editId ? 'notes/edit' : 'notes/create', postData, token).then(() => { + misskeyApi(postData.editId ? 'notes/edit' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted.value = true; } else { diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue index b2597d090b..82ed809650 100644 --- a/packages/frontend/src/components/MkPostFormAttaches.vue +++ b/packages/frontend/src/components/MkPostFormAttaches.vue @@ -24,6 +24,7 @@ import { defineAsyncComponent, inject } from 'vue'; import * as Misskey from 'misskey-js'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); @@ -61,7 +62,7 @@ function toggleSensitive(file) { return; } - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, isSensitive: !file.isSensitive, }).then(() => { @@ -78,7 +79,7 @@ async function rename(file) { allowEmpty: false, }); if (canceled) return; - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, name: result, }).then(() => { @@ -96,7 +97,7 @@ async function describe(file) { }, { done: caption => { let comment = caption.length === 0 ? null : caption; - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, comment: comment, }).then(() => { diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue index ebbd5e6cdc..1b8263ae67 100644 --- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue +++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue @@ -45,7 +45,8 @@ import { ref } from 'vue'; import { $i, getAccounts } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; import { instance } from '@/instance.js'; -import { api, apiWithDialog, promiseDialog } from '@/os.js'; +import { apiWithDialog, promiseDialog } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; defineProps<{ @@ -82,7 +83,7 @@ function subscribe() { pushSubscription.value = subscription; // Register - pushRegistrationInServer.value = await api('sw/register', { + pushRegistrationInServer.value = await misskeyApi('sw/register', { endpoint: subscription.endpoint, auth: encode(subscription.getKey('auth')), publickey: encode(subscription.getKey('p256dh')), @@ -159,7 +160,7 @@ if (navigator.serviceWorker == null) { supported.value = true; if (pushSubscription.value) { - const res = await api('sw/show-registration', { + const res = await misskeyApi('sw/show-registration', { endpoint: pushSubscription.value.endpoint, }); diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index c6ef05acef..a3791aee07 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -22,6 +22,7 @@ import * as Misskey from 'misskey-js'; import XDetails from '@/components/MkReactionsViewer.details.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import * as os from '@/os.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import { $i } from '@/account.js'; import MkReactionEffect from '@/components/MkReactionEffect.vue'; @@ -69,11 +70,11 @@ async function toggleReaction() { return; } - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: props.note.id, }).then(() => { if (oldReaction !== props.reaction) { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: props.reaction, }); @@ -87,7 +88,7 @@ async function toggleReaction() { return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: props.reaction, }); @@ -117,7 +118,7 @@ onMounted(() => { if (!mock) { useTooltip(buttonEl, async (showing) => { - const reactions = await os.apiGet('notes/reactions', { + const reactions = await misskeyApiGet('notes/reactions', { noteId: props.note.id, type: props.reaction, limit: 10, diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue index e69aa1be80..ef497e0e82 100644 --- a/packages/frontend/src/components/MkRetentionHeatmap.vue +++ b/packages/frontend/src/components/MkRetentionHeatmap.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, nextTick, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { alpha } from '@/scripts/color.js'; @@ -43,7 +43,7 @@ async function renderChart() { const maxDays = wide ? 10 : narrow ? 5 : 7; - let raw = await os.api('retention', { }); + let raw = await misskeyApi('retention', { }); raw = raw.slice(0, maxDays + 1); diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue index e2682ec06b..eb05878ae8 100644 --- a/packages/frontend/src/components/MkRetentionLineChart.vue +++ b/packages/frontend/src/components/MkRetentionLineChart.vue @@ -16,7 +16,7 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; import { alpha } from '@/scripts/color.js'; import { initChart } from '@/scripts/init-chart.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; initChart(); @@ -40,7 +40,7 @@ const getDate = (ymd: string) => { }; onMounted(async () => { - let raw = await os.api('retention', { }); + let raw = await misskeyApi('retention', { }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/components/MkRippleEffect.vue b/packages/frontend/src/components/MkRippleEffect.vue index 860b083327..11f1aec30f 100644 --- a/packages/frontend/src/components/MkRippleEffect.vue +++ b/packages/frontend/src/components/MkRippleEffect.vue @@ -77,7 +77,14 @@ const emit = defineEmits<{ (ev: 'end'): void; }>(); -const particles = []; +const particles: { + size: number; + xA: number; + yA: number; + xB: number; + yB: number; + color: string; +}[] = []; const origin = 64; const colors = ['#FF1493', '#00FFFF', '#FFE202']; const zIndex = os.claimZIndex('high'); diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue index c884ce53ea..278e2d6054 100644 --- a/packages/frontend/src/components/MkSignin.vue +++ b/packages/frontend/src/components/MkSignin.vue @@ -59,6 +59,7 @@ import MkInput from '@/components/MkInput.vue'; import MkInfo from '@/components/MkInfo.vue'; import { host as configHost } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; import { i18n } from '@/i18n.js'; @@ -95,7 +96,7 @@ const props = defineProps({ }); function onUsernameChange(): void { - os.api('users/show', { + misskeyApi('users/show', { username: username.value, }).then(userResponse => { user.value = userResponse; @@ -120,7 +121,7 @@ async function queryKey(): Promise<void> { credentialRequest.value = null; queryingKey.value = false; signing.value = true; - return os.api('signin', { + return misskeyApi('signin', { username: username.value, password: password.value, credential: credential.toJSON(), @@ -142,7 +143,7 @@ function onSubmit(): void { signing.value = true; if (!totpLogin.value && user.value && user.value.twoFactorEnabled) { if (webAuthnSupported() && user.value.securityKeys) { - os.api('signin', { + misskeyApi('signin', { username: username.value, password: password.value, }).then(res => { @@ -159,7 +160,7 @@ function onSubmit(): void { signing.value = false; } } else { - os.api('signin', { + misskeyApi('signin', { username: username.value, password: password.value, token: user.value?.twoFactorEnabled ? token.value : undefined, diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 9984b09c1a..98eeaed066 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -67,6 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ph-chalkboard-teacher ph-bold ph-lg"></i></template> </MkInput> <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;"> @@ -88,6 +89,7 @@ import MkInput from './MkInput.vue'; import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue'; import * as config from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; @@ -122,6 +124,7 @@ const passwordStrength = ref<'' | 'low' | 'medium' | 'high'>(''); const passwordRetypeState = ref<null | 'match' | 'not-match'>(null); const submitting = ref<boolean>(false); const hCaptchaResponse = ref<string | null>(null); +const mCaptchaResponse = ref<string | null>(null); const reCaptchaResponse = ref<string | null>(null); const turnstileResponse = ref<string | null>(null); const usernameAbortController = ref<null | AbortController>(null); @@ -130,6 +133,7 @@ const emailAbortController = ref<null | AbortController>(null); const shouldDisableSubmitting = computed((): boolean => { return submitting.value || instance.enableHcaptcha && !hCaptchaResponse.value || + instance.enableMcaptcha && !mCaptchaResponse.value || instance.enableRecaptcha && !reCaptchaResponse.value || instance.enableTurnstile && !turnstileResponse.value || instance.emailRequiredForSignup && emailState.value !== 'ok' || @@ -186,7 +190,7 @@ function onChangeUsername(): void { usernameState.value = 'wait'; usernameAbortController.value = new AbortController(); - os.api('username/available', { + misskeyApi('username/available', { username: username.value, }, undefined, usernameAbortController.value.signal).then(result => { usernameState.value = result.available ? 'ok' : 'unavailable'; @@ -209,7 +213,7 @@ function onChangeEmail(): void { emailState.value = 'wait'; emailAbortController.value = new AbortController(); - os.api('email-address/available', { + misskeyApi('email-address/available', { emailAddress: email.value, }, undefined, emailAbortController.value.signal).then(result => { emailState.value = result.available ? 'ok' : @@ -251,13 +255,14 @@ async function onSubmit(): Promise<void> { submitting.value = true; try { - await os.api('signup', { + await misskeyApi('signup', { username: username.value, password: password.value, emailAddress: email.value, invitationCode: invitationCode.value, reason: reason.value, 'hcaptcha-response': hCaptchaResponse.value, + 'm-captcha-response': mCaptchaResponse.value, 'g-recaptcha-response': reCaptchaResponse.value, }); if (instance.emailRequiredForSignup) { @@ -275,7 +280,7 @@ async function onSubmit(): Promise<void> { }); emit('approvalPending'); } else { - const res = await os.api('signin', { + const res = await misskeyApi('signin', { username: username.value, password: password.value, }); diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index c071fb938a..1ad213912f 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -48,7 +48,7 @@ import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; import { defaultStore } from '@/store.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/global/router/supplier.js'; import * as os from '@/os.js'; import { checkAnimationFromMfm } from '@/scripts/check-animated-mfm.js'; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 8bd68c0fd2..00be5d2042 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -11,13 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only :pagination="paginationQuery" :noGap="!defaultStore.state.showGapBetweenNotesInTimeline" @queue="emit('queue', $event)" - @status="prComponent.setDisabled($event)" + @status="prComponent?.setDisabled($event)" /> </MkPullToRefresh> </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, ref } from 'vue'; +import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue'; import { Connection } from 'misskey-js/built/streaming.js'; import MkNotes from '@/components/MkNotes.vue'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; @@ -65,12 +65,14 @@ type TimelineQueryType = { roleId?: string } -const prComponent = ref<InstanceType<typeof MkPullToRefresh>>(); -const tlComponent = ref<InstanceType<typeof MkNotes>>(); +const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); +const tlComponent = shallowRef<InstanceType<typeof MkNotes>>(); let tlNotesCount = 0; -const prepend = note => { +function prepend(note) { + if (tlComponent.value == null) return; + tlNotesCount++; if (instance.notesPerOneAd > 0 && tlNotesCount % instance.notesPerOneAd === 0) { @@ -84,7 +86,7 @@ const prepend = note => { if (props.sound) { sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note'); } -}; +} let connection: Connection; let connection2: Connection; @@ -142,6 +144,7 @@ function connectChannel() { connection.on('mention', onNote); } else if (props.src === 'list') { connection = stream.useChannel('userList', { + withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }); @@ -219,6 +222,7 @@ function updatePaginationQuery() { } else if (props.src === 'list') { endpoint = 'notes/user-list-timeline'; query = { + withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }; @@ -257,8 +261,9 @@ function refreshEndpointAndChannel() { updatePaginationQuery(); } +// デッキのリストカラムでwithRenotesを変更した場合に自動的に更新されるようにさせる // IDが切り替わったら切り替え先のTLを表示させたい -watch(() => [props.list, props.antenna, props.channel, props.role], refreshEndpointAndChannel); +watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel); // 初回表示用 refreshEndpointAndChannel(); @@ -269,6 +274,8 @@ onUnmounted(() => { function reloadTimeline() { return new Promise<void>((res) => { + if (tlComponent.value == null) return; + tlNotesCount = 0; tlComponent.value.pagingComponent?.reload().then(() => { diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue index 8e8e26ed5f..d024e1e593 100644 --- a/packages/frontend/src/components/MkTokenGenerateWindow.vue +++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withOkButton="true" :okButtonDisabled="false" :canClose="false" - @close="dialog.close()" + @close="dialog?.close()" @closed="$emit('closed')" @ok="ok()" > @@ -87,7 +87,7 @@ function ok(): void { name: name.value, permissions: Object.keys(permissions.value).filter(p => permissions.value[p]), }); - dialog.value.close(); + dialog.value?.close(); } function disableAll(): void { diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue index eeb9325a29..27e5bb1b84 100644 --- a/packages/frontend/src/components/MkTooltip.vue +++ b/packages/frontend/src/components/MkTooltip.vue @@ -13,8 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only > <div v-show="showing" ref="el" :class="$style.root" class="_acrylic _shadow" :style="{ zIndex, maxWidth: maxWidth + 'px' }"> <slot> - <Mfm v-if="asMfm" :text="text"/> - <span v-else>{{ text }}</span> + <template v-if="text"> + <Mfm v-if="asMfm" :text="text"/> + <span v-else>{{ text }}</span> + </template> </slot> </div> </Transition> @@ -53,6 +55,7 @@ const el = shallowRef<HTMLElement>(); const zIndex = os.claimZIndex('high'); function setPosition() { + if (!el.value || !props.targetElement) return; const data = calcPopupPosition(el.value, { anchorElement: props.targetElement, direction: props.direction, diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue index 07efaf8982..b5f657e951 100644 --- a/packages/frontend/src/components/MkUpdated.vue +++ b/packages/frontend/src/components/MkUpdated.vue @@ -4,12 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkModal ref="modal" :zPriority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')"> +<MkModal ref="modal" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')"> <div :class="$style.root"> <div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div> <div :class="$style.version">✨{{ version }}🚀</div> <MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton> - <MkButton :class="$style.gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton> + <MkButton :class="$style.gotIt" primary full @click="modal?.close()">{{ i18n.ts.gotIt }}</MkButton> </div> </MkModal> </template> @@ -26,7 +26,7 @@ import { confetti } from '@/scripts/confetti.js'; const modal = shallowRef<InstanceType<typeof MkModal>>(); const whatIsNew = () => { - modal.value.close(); + modal.value?.close(); window.open(`https://git.joinsharkey.org/Sharkey/Sharkey/releases/tag/${version}`, '_blank'); }; diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 486aaa0bbd..81d0acb8fa 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -84,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { defineAsyncComponent, onUnmounted, ref } from 'vue'; -import type { summaly } from 'summaly'; +import type { summaly } from '@misskey-dev/summaly'; import { url as local } from '@/config.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue index 3fbadbe34f..17ede3e620 100644 --- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue +++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue @@ -56,6 +56,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkTextarea from '@/components/MkTextarea.vue'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -121,7 +122,7 @@ async function del() { }); if (canceled) return; - os.api('admin/announcements/delete', { + misskeyApi('admin/announcements/delete', { id: props.announcement.id, }).then(() => { emit('done', { diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue index b9c7377972..6068d5d13b 100644 --- a/packages/frontend/src/components/MkUserCardMini.vue +++ b/packages/frontend/src/components/MkUserCardMini.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only import * as Misskey from 'misskey-js'; import { onMounted, ref } from 'vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; -import * as os from '@/os.js'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; import { acct } from '@/filters/user.js'; const props = withDefaults(defineProps<{ @@ -32,7 +32,7 @@ const chartValues = ref<number[] | null>(null); onMounted(() => { if (props.withChart) { - os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { + misskeyApiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => { // 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く res.inc.splice(0, 1); chartValues.value = res.inc; diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index c9100652f3..0795b43bfd 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -72,6 +72,7 @@ import * as Misskey from 'misskey-js'; import MkFollowButton from '@/components/MkFollowButton.vue'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { getUserMenu } from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; import { i18n } from '@/i18n.js'; @@ -109,7 +110,7 @@ onMounted(() => { Misskey.acct.parse(props.q.substring(1)) : { userId: props.q }; - os.api('users/show', query).then(res => { + misskeyApi('users/show', query).then(res => { if (!props.showing) return; user.value = res; }); diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 9d41147bd2..f4aa06950d 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -62,7 +62,7 @@ import * as Misskey from 'misskey-js'; import MkInput from '@/components/MkInput.vue'; import FormSplit from '@/components/form/split.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; @@ -90,7 +90,7 @@ const search = () => { users.value = []; return; } - os.api('users/search-by-username-and-host', { + misskeyApi('users/search-by-username-and-host', { username: username.value, host: host.value, limit: 10, @@ -118,7 +118,7 @@ const cancel = () => { }; onMounted(() => { - os.api('users/show', { + misskeyApi('users/show', { userIds: defaultStore.state.recentlyUsedUsers, }).then(users => { if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) { diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue index 664c4da203..ef9f74b95b 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue @@ -41,14 +41,14 @@ import { i18n } from '@/i18n.js'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkFolder from '@/components/MkFolder.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const isLocked = ref(false); const hideOnlineStatus = ref(false); const noCrawle = ref(false); watch([isLocked, hideOnlineStatus, noCrawle], () => { - os.api('i/update', { + misskeyApi('i/update', { isLocked: !!isLocked.value, hideOnlineStatus: !!hideOnlineStatus.value, noCrawle: !!noCrawle.value, diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue index 7becc5c66d..695c0c7843 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.User.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue @@ -29,7 +29,7 @@ import * as Misskey from 'misskey-js'; import { ref } from 'vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ user: Misskey.entities.UserDetailed; @@ -39,7 +39,7 @@ const isFollowing = ref(false); async function follow() { isFollowing.value = true; - os.api('following/create', { + misskeyApi('following/create', { userId: props.user.id, }); } diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue index 746ed3e0de..e45d594f12 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue @@ -17,7 +17,7 @@ import { onMounted, shallowRef, ref } from 'vue'; import { Chart } from 'chart.js'; import gradient from 'chartjs-plugin-gradient'; import tinycolor from 'tinycolor2'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { useChartTooltip } from '@/scripts/use-chart-tooltip.js'; import { chartVLine } from '@/scripts/chart-vline.js'; @@ -53,7 +53,7 @@ async function renderChart() { })); }; - const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' }); + const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' }); const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)'; diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 862a38bd54..9f4fc00938 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -63,6 +63,7 @@ import MkTimeline from '@/components/MkTimeline.vue'; import MkInfo from '@/components/MkInfo.vue'; import { instanceName } from '@/config.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; import MkNumber from '@/components/MkNumber.vue'; @@ -71,11 +72,11 @@ import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart. const meta = ref<Misskey.entities.MetaResponse | null>(null); const stats = ref<Misskey.entities.StatsResponse | null>(null); -os.api('meta', { detail: true }).then(_meta => { +misskeyApi('meta', { detail: true }).then(_meta => { meta.value = _meta; }); -os.api('stats', {}).then((res) => { +misskeyApi('stats', {}).then((res) => { stats.value = res; }); diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue index e5b8bd9b15..e897a0b7a6 100644 --- a/packages/frontend/src/components/MkWindow.vue +++ b/packages/frontend/src/components/MkWindow.vue @@ -143,6 +143,7 @@ function top() { } function maximize() { + if (rootEl.value == null) return; maximized.value = true; unResizedTop = rootEl.value.style.top; unResizedLeft = rootEl.value.style.left; @@ -155,6 +156,7 @@ function maximize() { } function unMaximize() { + if (rootEl.value == null) return; maximized.value = false; rootEl.value.style.top = unResizedTop; rootEl.value.style.left = unResizedLeft; @@ -163,6 +165,7 @@ function unMaximize() { } function minimize() { + if (rootEl.value == null) return; minimized.value = true; unResizedWidth = rootEl.value.style.width; unResizedHeight = rootEl.value.style.height; @@ -171,8 +174,8 @@ function minimize() { } function unMinimize() { + if (rootEl.value == null) return; const main = rootEl.value; - if (main == null) return; minimized.value = false; rootEl.value.style.width = unResizedWidth; diff --git a/packages/frontend/src/components/SkApprovalUser.vue b/packages/frontend/src/components/SkApprovalUser.vue index 2bf6361ac8..f85944cd04 100644 --- a/packages/frontend/src/components/SkApprovalUser.vue +++ b/packages/frontend/src/components/SkApprovalUser.vue @@ -33,6 +33,7 @@ import MkFolder from '@/components/MkFolder.vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ user: Misskey.entities.User; @@ -42,7 +43,7 @@ let reason = ref(''); let email = ref(''); function getReason() { - return os.api('admin/show-user', { + return misskeyApi('admin/show-user', { userId: props.user.id, }).then(info => { reason.value = info?.signupReason; @@ -87,7 +88,7 @@ async function approveAccount() { text: i18n.ts.approveConfirm, }); if (confirm.canceled) return; - await os.api('admin/approve-user', { userId: props.user.id }); + await misskeyApi('admin/approve-user', { userId: props.user.id }); emits('deleted', props.user.id); } </script> diff --git a/packages/frontend/src/components/SkNote.vue b/packages/frontend/src/components/SkNote.vue index 1603b6acb2..66ef22633d 100644 --- a/packages/frontend/src/components/SkNote.vue +++ b/packages/frontend/src/components/SkNote.vue @@ -190,6 +190,7 @@ import { focusPrev, focusNext } from '@/scripts/focus.js'; import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -208,7 +209,7 @@ import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/global/router/supplier.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -320,7 +321,7 @@ const keymap = { }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -341,7 +342,7 @@ if (props.mock) { if (!props.mock) { useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -359,7 +360,7 @@ if (!props.mock) { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -378,7 +379,7 @@ if (!props.mock) { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -453,7 +454,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -479,7 +480,7 @@ function renote(visibility: Visibility | 'local') { } if (!props.mock) { - os.api('notes/create', { + misskeyApi('notes/create', { localOnly: visibility === 'local' ? true : localOnlySetting, visibility: noteVisibility, renoteId: appearNote.value.id, @@ -503,7 +504,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -525,7 +526,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -567,7 +568,7 @@ function like(): void { if (props.mock) { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -590,7 +591,7 @@ function react(viaKeyboard = false): void { return; } - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -611,7 +612,7 @@ function react(viaKeyboard = false): void { return; } - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -633,7 +634,7 @@ function undoReact(note): void { return; } - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } @@ -642,7 +643,7 @@ function undoRenote(note) : void { if (props.mock) { return; } - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: note.id, }); os.toast(i18n.ts.rmboost); @@ -719,7 +720,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -781,7 +782,7 @@ function scrollIntoView() { } function readPromo() { - os.api('promo/read', { + misskeyApi('promo/read', { noteId: appearNote.value.id, }); isDeleted.value = true; diff --git a/packages/frontend/src/components/SkNoteDetailed.vue b/packages/frontend/src/components/SkNoteDetailed.vue index 4a06e8f56a..212fa99bef 100644 --- a/packages/frontend/src/components/SkNoteDetailed.vue +++ b/packages/frontend/src/components/SkNoteDetailed.vue @@ -245,6 +245,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js'; import { userPage } from '@/filters/user.js'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { defaultStore, noteViewInterruptors } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; @@ -336,7 +337,7 @@ watch(() => props.expandAllCws, (expandAllCws) => { }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -355,7 +356,7 @@ const keymap = { }; provide('react', (reaction: string) => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -403,7 +404,7 @@ useNoteCapture({ }); useTooltip(renoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, }); @@ -421,7 +422,7 @@ useTooltip(renoteButton, async (showing) => { }); useTooltip(quoteButton, async (showing) => { - const renotes = await os.api('notes/renotes', { + const renotes = await misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 11, quote: true, @@ -502,7 +503,7 @@ function renote(visibility: Visibility | 'local') { os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: appearNote.value.id, channelId: appearNote.value.channelId, }).then(() => { @@ -526,7 +527,7 @@ function renote(visibility: Visibility | 'local') { noteVisibility = smallerVisibility(visibility === 'local' || visibility === 'specified' ? appearNote.value.visibility : visibility, 'home'); } - os.api('notes/create', { + misskeyApi('notes/create', { localOnly: visibility === 'local' ? true : localOnlySetting, visibility: noteVisibility, renoteId: appearNote.value.id, @@ -546,7 +547,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -568,7 +569,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -605,7 +606,7 @@ function react(viaKeyboard = false): void { pleaseLogin(); showMovedDialog(); if (appearNote.value.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -621,7 +622,7 @@ function react(viaKeyboard = false): void { reactionPicker.show(reactButton.value, reaction => { sound.play('reaction'); - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: appearNote.value.id, reaction: reaction, }); @@ -638,7 +639,7 @@ function like(): void { pleaseLogin(); showMovedDialog(); sound.play('reaction'); - os.api('notes/like', { + misskeyApi('notes/like', { noteId: appearNote.value.id, override: defaultLike.value, }); @@ -654,14 +655,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -721,7 +722,7 @@ function showRenoteMenu(viaKeyboard = false): void { icon: 'ph-trash ph-bold ph-lg', danger: true, action: () => { - os.api('notes/delete', { + misskeyApi('notes/delete', { noteId: note.value.id, }); isDeleted.value = true; @@ -743,7 +744,7 @@ const repliesLoaded = ref(false); function loadReplies() { repliesLoaded.value = true; - os.api('notes/children', { + misskeyApi('notes/children', { noteId: appearNote.value.id, limit: 30, showQuotes: false, @@ -758,7 +759,7 @@ const quotesLoaded = ref(false); function loadQuotes() { quotesLoaded.value = true; - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, limit: 30, quote: true, @@ -773,7 +774,7 @@ const conversationLoaded = ref(false); function loadConversation() { conversationLoaded.value = true; - os.api('notes/conversation', { + misskeyApi('notes/conversation', { noteId: appearNote.value.replyId, }).then(res => { conversation.value = res.reverse(); diff --git a/packages/frontend/src/components/SkNoteHeader.vue b/packages/frontend/src/components/SkNoteHeader.vue index 7a3f9d02f5..d471c2e6df 100644 --- a/packages/frontend/src/components/SkNoteHeader.vue +++ b/packages/frontend/src/components/SkNoteHeader.vue @@ -82,7 +82,7 @@ import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js'; import SkInstanceTicker from '@/components/SkInstanceTicker.vue'; import { popupMenu } from '@/os.js'; import { defaultStore } from '@/store.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/global/router/supplier.js'; import { deviceKind } from '@/scripts/device-kind.js'; const props = defineProps<{ diff --git a/packages/frontend/src/components/SkNoteSub.vue b/packages/frontend/src/components/SkNoteSub.vue index 8b3ced3761..60a574731f 100644 --- a/packages/frontend/src/components/SkNoteSub.vue +++ b/packages/frontend/src/components/SkNoteSub.vue @@ -99,6 +99,7 @@ import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; import { notePage } from '@/filters/note.js'; import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; @@ -175,7 +176,7 @@ useNoteCapture({ }); if ($i) { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: appearNote.value.id, userId: $i.id, limit: 1, @@ -205,7 +206,7 @@ function react(viaKeyboard = false): void { showMovedDialog(); sound.play('reaction'); if (props.note.reactionAcceptance === 'likeOnly') { - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -219,7 +220,7 @@ function react(viaKeyboard = false): void { } else { blur(); reactionPicker.show(reactButton.value, reaction => { - os.api('notes/reactions/create', { + misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: reaction, }); @@ -236,7 +237,7 @@ function like(): void { pleaseLogin(); showMovedDialog(); sound.play('reaction'); - os.api('notes/like', { + misskeyApi('notes/like', { noteId: props.note.id, override: defaultLike.value, }); @@ -252,14 +253,14 @@ function like(): void { function undoReact(note): void { const oldReaction = note.myReaction; if (!oldReaction) return; - os.api('notes/reactions/delete', { + misskeyApi('notes/reactions/delete', { noteId: note.id, }); } function undoRenote() : void { if (!renoted.value) return; - os.api('notes/unrenote', { + misskeyApi('notes/unrenote', { noteId: appearNote.value.id, }); os.toast(i18n.ts.rmboost); @@ -333,7 +334,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: props.note.id, channelId: props.note.channelId, }).then(() => { @@ -349,7 +350,7 @@ function renote(visibility: 'public' | 'home' | 'followers' | 'specified' | 'loc os.popup(MkRippleEffect, { x, y }, {}, 'end'); } - os.api('notes/create', { + misskeyApi('notes/create', { renoteId: props.note.id, localOnly: visibility === 'local' ? true : false, visibility: visibility === 'local' || visibility === 'specified' ? props.note.visibility : visibility, @@ -369,7 +370,7 @@ function quote() { renote: appearNote.value, channel: appearNote.value.channel, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -391,7 +392,7 @@ function quote() { os.post({ renote: appearNote.value, }).then(() => { - os.api('notes/renotes', { + misskeyApi('notes/renotes', { noteId: props.note.id, userId: $i.id, limit: 1, @@ -420,7 +421,7 @@ function menu(viaKeyboard = false): void { } if (props.detail) { - os.api('notes/children', { + misskeyApi('notes/children', { noteId: props.note.id, limit: numberOfReplies.value, showQuotes: false, diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue index e2b59869a4..2119b33d07 100644 --- a/packages/frontend/src/components/global/MkA.vue +++ b/packages/frontend/src/components/global/MkA.vue @@ -15,7 +15,7 @@ import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { url } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { useRouter } from '@/router.js'; +import { useRouter } from '@/global/router/supplier.js'; const props = withDefaults(defineProps<{ to: string; diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue index f03bf26fad..8945f2b64b 100644 --- a/packages/frontend/src/components/global/MkEmoji.vue +++ b/packages/frontend/src/components/global/MkEmoji.vue @@ -5,15 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <img v-if="!useOsNativeEmojis" :class="$style.root" :src="url" :alt="props.emoji" decoding="async" @pointerenter="computeTitle" @click="onClick" v-on:click.stop/> -<span v-else-if="useOsNativeEmojis" :alt="props.emoji" @pointerenter="computeTitle" @click="onClick" v-on:click.stop>{{ props.emoji }}</span> -<span v-else>{{ emoji }}</span> +<span v-else :alt="props.emoji" @pointerenter="computeTitle" @click="onClick" v-on:click.stop>{{ colorizedNativeEmoji }}</span> </template> <script lang="ts" setup> import { computed, inject } from 'vue'; import { char2twemojiFilePath, char2fluentEmojiFilePath, char2tossfaceFilePath } from '@/scripts/emoji-base.js'; import { defaultStore } from '@/store.js'; -import { getEmojiName } from '@/scripts/emojilist.js'; +import { colorizeEmoji, getEmojiName } from '@/scripts/emojilist.js'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import * as sound from '@/scripts/sound.js'; @@ -30,9 +29,8 @@ const react = inject<((name: string) => void) | null>('react', null); const char2path = defaultStore.state.emojiStyle === 'twemoji' ? char2twemojiFilePath : defaultStore.reactiveState.emojiStyle.value === 'tossface' ? char2tossfaceFilePath : char2fluentEmojiFilePath; const useOsNativeEmojis = computed(() => defaultStore.state.emojiStyle === 'native'); -const url = computed(() => { - return char2path(props.emoji); -}); +const url = computed(() => char2path(props.emoji)); +const colorizedNativeEmoji = computed(() => colorizeEmoji(props.emoji)); // Searching from an array with 2000 items for every emoji felt like too energy-consuming, so I decided to do it lazily on pointerenter function computeTitle(event: PointerEvent): void { diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index a3bfdf0bb4..25118a9cbc 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -48,6 +48,10 @@ type MfmEvents = { clickEv(id: string): void; }; +type MfmEvents = { + clickEv(id: string): void; +}; + // eslint-disable-next-line import/no-default-export export default function(props: MfmProps, context: SetupContext<MfmEvents>) { const isNote = props.isNote ?? true; diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue index 99ed8adbef..dc7474835d 100644 --- a/packages/frontend/src/components/global/RouterView.vue +++ b/packages/frontend/src/components/global/RouterView.vue @@ -16,12 +16,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, onBeforeUnmount, provide, shallowRef, ref } from 'vue'; -import { Resolved, Router } from '@/nirax.js'; +import { inject, onBeforeUnmount, provide, ref, shallowRef } from 'vue'; +import { IRouter, Resolved } from '@/nirax.js'; import { defaultStore } from '@/store.js'; const props = defineProps<{ - router?: Router; + router?: IRouter; }>(); const router = props.router ?? inject('router'); diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue index d885ebb1d6..83fdf24deb 100644 --- a/packages/frontend/src/components/page/page.note.vue +++ b/packages/frontend/src/components/page/page.note.vue @@ -16,7 +16,7 @@ import * as Misskey from 'misskey-js'; import { NoteBlock } from './block.type.js'; import MkNote from '@/components/MkNote.vue'; import MkNoteDetailed from '@/components/MkNoteDetailed.vue'; -import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = defineProps<{ block: NoteBlock, @@ -26,7 +26,7 @@ const props = defineProps<{ const note = ref<Misskey.entities.Note | null>(null); onMounted(() => { - os.api('notes/show', { noteId: props.block.note }) + misskeyApi('notes/show', { noteId: props.block.note }) .then(result => { note.value = result; }); |