Merge branch 'develop' into pag-back
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import fs from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import path from 'node:path';
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import type { entities } from 'misskey-js'
|
||||
|
||||
export function abuseUserReport() {
|
||||
@@ -110,6 +115,7 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host = 'mi
|
||||
publicReactions: false,
|
||||
securityKeys: false,
|
||||
twoFactorEnabled: false,
|
||||
twoFactorBackupCodesStock: 'none',
|
||||
updatedAt: null,
|
||||
uri: null,
|
||||
url: null,
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import { basename, dirname } from 'node:path/posix';
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import type { StorybookConfig } from '@storybook/vue3-vite';
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { addons } from '@storybook/manager-api';
|
||||
import { create } from '@storybook/theming/create';
|
||||
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { type SharedOptions, rest } from 'msw';
|
||||
|
||||
export const onUnhandledRequest = ((req, print) => {
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { writeFile } from 'node:fs/promises';
|
||||
import locales from '../../../locales/index.js';
|
||||
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { readFile, writeFile } from 'node:fs/promises';
|
||||
import JSON5 from 'json5';
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
|
||||
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.21.0/tabler-icons.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@2.32.0/tabler-icons.min.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
|
||||
<style>
|
||||
html {
|
||||
|
@@ -1,3 +1,8 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and other misskey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { addons } from '@storybook/addons';
|
||||
import { FORCE_REMOUNT } from '@storybook/core-events';
|
||||
import { type Preview, setup } from '@storybook/vue3';
|
||||
|
@@ -6,6 +6,7 @@
|
||||
"allowUnusedLabels": false,
|
||||
"allowUnreachableCode": false,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noEmitOnError": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
|
@@ -5,7 +5,7 @@
|
||||
"watch": "vite",
|
||||
"build": "vite build",
|
||||
"storybook-dev": "nodemon --verbose --watch src --ext \"mdx,ts,vue\" --ignore \"*.stories.ts\" --exec \"pnpm build-storybook-pre && pnpm exec storybook dev -p 6006 --ci\"",
|
||||
"build-storybook-pre": "tsc -p .storybook && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
"build-storybook-pre": "(tsc -p .storybook || echo done.) && node .storybook/generate.js && node .storybook/preload-locale.js && node .storybook/preload-theme.js",
|
||||
"build-storybook": "pnpm build-storybook-pre && storybook build",
|
||||
"chromatic": "chromatic",
|
||||
"test": "vitest --run",
|
||||
@@ -16,29 +16,30 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@discordapp/twemoji": "14.1.2",
|
||||
"@github/webauthn-json": "2.1.1",
|
||||
"@rollup/plugin-alias": "5.0.0",
|
||||
"@rollup/plugin-json": "6.0.0",
|
||||
"@rollup/plugin-replace": "5.0.2",
|
||||
"@rollup/pluginutils": "5.0.2",
|
||||
"@rollup/pluginutils": "5.0.4",
|
||||
"@syuilo/aiscript": "0.15.0",
|
||||
"@tabler/icons-webfont": "2.30.0",
|
||||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vue-macros/reactivity-transform": "0.3.16",
|
||||
"@tabler/icons-webfont": "2.32.0",
|
||||
"@vitejs/plugin-vue": "4.3.4",
|
||||
"@vue-macros/reactivity-transform": "0.3.22",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"astring": "1.8.6",
|
||||
"autosize": "6.0.1",
|
||||
"broadcast-channel": "5.1.0",
|
||||
"broadcast-channel": "5.3.0",
|
||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
|
||||
"buraha": "0.0.1",
|
||||
"canvas-confetti": "1.6.0",
|
||||
"chart.js": "4.3.2",
|
||||
"chart.js": "4.4.0",
|
||||
"chartjs-adapter-date-fns": "3.0.0",
|
||||
"chartjs-chart-matrix": "2.0.1",
|
||||
"chartjs-plugin-gradient": "0.6.1",
|
||||
"chartjs-plugin-zoom": "2.0.1",
|
||||
"chromatic": "6.19.9",
|
||||
"compare-versions": "6.0.0",
|
||||
"cropperjs": "2.0.0-beta.3",
|
||||
"chromatic": "6.24.1",
|
||||
"compare-versions": "6.1.0",
|
||||
"cropperjs": "2.0.0-beta.4",
|
||||
"date-fns": "2.30.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
@@ -51,93 +52,89 @@
|
||||
"matter-js": "0.19.0",
|
||||
"mfm-js": "0.23.3",
|
||||
"misskey-js": "workspace:*",
|
||||
"photoswipe": "5.3.8",
|
||||
"photoswipe": "5.3.9",
|
||||
"prismjs": "1.29.0",
|
||||
"punycode": "2.3.0",
|
||||
"querystring": "0.2.1",
|
||||
"rollup": "3.27.0",
|
||||
"rollup": "3.28.1",
|
||||
"s-age": "1.1.2",
|
||||
"sanitize-html": "2.11.0",
|
||||
"sass": "1.64.1",
|
||||
"sass": "1.66.1",
|
||||
"strict-event-emitter-types": "2.0.0",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.155.0",
|
||||
"three": "0.156.0",
|
||||
"throttle-debounce": "5.0.0",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tsc-alias": "1.8.7",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"twemoji-parser": "14.0.0",
|
||||
"typescript": "5.1.6",
|
||||
"typescript": "5.2.2",
|
||||
"ua-parser-js": "2.0.0-alpha.2",
|
||||
"uuid": "9.0.0",
|
||||
"vanilla-tilt": "1.8.0",
|
||||
"vite": "4.4.7",
|
||||
"vanilla-tilt": "1.8.1",
|
||||
"vite": "4.4.9",
|
||||
"vue": "3.3.4",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "7.0.27",
|
||||
"@storybook/addon-essentials": "7.0.27",
|
||||
"@storybook/addon-interactions": "7.0.27",
|
||||
"@storybook/addon-links": "7.0.27",
|
||||
"@storybook/addon-storysource": "7.0.27",
|
||||
"@storybook/addons": "7.0.27",
|
||||
"@storybook/blocks": "7.0.27",
|
||||
"@storybook/core-events": "7.0.27",
|
||||
"@storybook/jest": "0.1.0",
|
||||
"@storybook/manager-api": "7.0.27",
|
||||
"@storybook/preview-api": "7.0.27",
|
||||
"@storybook/react": "7.0.27",
|
||||
"@storybook/react-vite": "7.0.27",
|
||||
"@storybook/addon-actions": "7.4.0",
|
||||
"@storybook/addon-essentials": "7.4.0",
|
||||
"@storybook/addon-interactions": "7.4.0",
|
||||
"@storybook/addon-links": "7.4.0",
|
||||
"@storybook/addon-storysource": "7.4.0",
|
||||
"@storybook/addons": "7.4.0",
|
||||
"@storybook/blocks": "7.4.0",
|
||||
"@storybook/core-events": "7.4.0",
|
||||
"@storybook/jest": "0.2.2",
|
||||
"@storybook/manager-api": "7.4.0",
|
||||
"@storybook/preview-api": "7.4.0",
|
||||
"@storybook/react": "7.4.0",
|
||||
"@storybook/react-vite": "7.4.0",
|
||||
"@storybook/testing-library": "0.2.0",
|
||||
"@storybook/theming": "7.0.27",
|
||||
"@storybook/types": "7.0.27",
|
||||
"@storybook/vue3": "7.0.27",
|
||||
"@storybook/vue3-vite": "7.0.27",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@storybook/theming": "7.4.0",
|
||||
"@storybook/types": "7.4.0",
|
||||
"@storybook/vue3": "7.4.0",
|
||||
"@storybook/vue3-vite": "7.4.0",
|
||||
"@testing-library/vue": "7.0.0",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/estree": "1.0.1",
|
||||
"@types/gulp": "4.0.13",
|
||||
"@types/gulp-rename": "2.0.2",
|
||||
"@types/matter-js": "0.18.5",
|
||||
"@types/matter-js": "0.19.0",
|
||||
"@types/micromatch": "4.0.2",
|
||||
"@types/node": "20.4.5",
|
||||
"@types/node": "20.5.9",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/sanitize-html": "2.9.0",
|
||||
"@types/testing-library__jest-dom": "5.14.9",
|
||||
"@types/throttle-debounce": "5.0.0",
|
||||
"@types/tinycolor2": "1.4.3",
|
||||
"@types/uuid": "9.0.2",
|
||||
"@types/websocket": "1.0.5",
|
||||
"@types/uuid": "9.0.3",
|
||||
"@types/websocket": "1.0.6",
|
||||
"@types/ws": "8.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "6.2.0",
|
||||
"@typescript-eslint/parser": "6.2.0",
|
||||
"@vitest/coverage-v8": "0.33.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.6.0",
|
||||
"@typescript-eslint/parser": "6.6.0",
|
||||
"@vitest/coverage-v8": "0.34.3",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"acorn": "8.10.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.17.2",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-plugin-import": "2.28.0",
|
||||
"eslint-plugin-vue": "9.16.1",
|
||||
"cypress": "13.1.0",
|
||||
"eslint": "8.48.0",
|
||||
"eslint-plugin-import": "2.28.1",
|
||||
"eslint-plugin-vue": "9.17.0",
|
||||
"fast-glob": "3.3.1",
|
||||
"happy-dom": "10.0.3",
|
||||
"micromatch": "4.0.5",
|
||||
"msw": "1.2.3",
|
||||
"msw": "1.3.0",
|
||||
"msw-storybook-addon": "1.8.0",
|
||||
"nodemon": "3.0.1",
|
||||
"prettier": "3.0.0",
|
||||
"prettier": "3.0.3",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"start-server-and-test": "2.0.0",
|
||||
"storybook": "7.0.27",
|
||||
"storybook": "7.4.0",
|
||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||
"summaly": "github:misskey-dev/summaly",
|
||||
"vite-plugin-turbosnap": "1.0.2",
|
||||
"vitest": "0.33.0",
|
||||
"vitest": "0.34.3",
|
||||
"vitest-fetch-mock": "0.2.2",
|
||||
"vue-eslint-parser": "9.3.1",
|
||||
"vue-tsc": "1.8.8"
|
||||
|
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
import { defineAsyncComponent, reactive, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { showSuspendedDialog } from './scripts/show-suspended-dialog';
|
||||
import { i18n } from './i18n';
|
||||
import { miLocalStorage } from './local-storage';
|
||||
@@ -16,7 +16,7 @@ import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
||||
type Account = misskey.entities.MeDetailed;
|
||||
type Account = Misskey.entities.MeDetailed;
|
||||
|
||||
const accountData = miLocalStorage.getItem('account');
|
||||
|
||||
@@ -211,8 +211,8 @@ export async function login(token: Account['token'], redirect?: string) {
|
||||
export async function openAccountMenu(opts: {
|
||||
includeCurrentAccount?: boolean;
|
||||
withExtraOperation: boolean;
|
||||
active?: misskey.entities.UserDetailed['id'];
|
||||
onChoose?: (account: misskey.entities.UserDetailed) => void;
|
||||
active?: Misskey.entities.UserDetailed['id'];
|
||||
onChoose?: (account: Misskey.entities.UserDetailed) => void;
|
||||
}, ev: MouseEvent) {
|
||||
if (!$i) return;
|
||||
|
||||
@@ -234,7 +234,7 @@ export async function openAccountMenu(opts: {
|
||||
}, 'closed');
|
||||
}
|
||||
|
||||
async function switchAccount(account: misskey.entities.UserDetailed) {
|
||||
async function switchAccount(account: Misskey.entities.UserDetailed) {
|
||||
const storedAccounts = await getAccounts();
|
||||
const found = storedAccounts.find(x => x.id === account.id);
|
||||
if (found == null) return;
|
||||
@@ -248,7 +248,7 @@ export async function openAccountMenu(opts: {
|
||||
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
|
||||
const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
||||
|
||||
function createItem(account: misskey.entities.UserDetailed) {
|
||||
function createItem(account: Misskey.entities.UserDetailed) {
|
||||
return {
|
||||
type: 'user' as const,
|
||||
user: account,
|
||||
|
@@ -3,10 +3,11 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { Cache } from '@/scripts/cache';
|
||||
import { api } from '@/os';
|
||||
|
||||
export const clipsCache = new Cache<misskey.entities.Clip[]>(Infinity);
|
||||
export const rolesCache = new Cache(Infinity);
|
||||
export const userListsCache = new Cache<misskey.entities.UserList[]>(Infinity);
|
||||
export const antennasCache = new Cache<misskey.entities.Antenna[]>(Infinity);
|
||||
export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => api('clips/list'));
|
||||
export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list'));
|
||||
export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => api('users/lists/list'));
|
||||
export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => api('antennas/list'));
|
||||
|
@@ -13,13 +13,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { UserLite } from 'misskey-js/built/entities';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkMention from './MkMention.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import { host as localHost } from '@/config';
|
||||
import { api } from '@/os';
|
||||
|
||||
const user = ref<UserLite>();
|
||||
const user = ref<Misskey.entities.UserLite>();
|
||||
|
||||
const props = defineProps<{
|
||||
movedTo: string; // user id
|
||||
|
@@ -52,14 +52,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { onMounted } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
withLocked: boolean;
|
||||
withDescription: boolean;
|
||||
}>(), {
|
||||
|
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, shallowRef } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
@@ -31,7 +31,7 @@ import { i18n } from '@/i18n';
|
||||
import { $i, updateAccount } from '@/account';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
announcement: misskey.entities.Announcement;
|
||||
announcement: Misskey.entities.Announcement;
|
||||
}>(), {
|
||||
});
|
||||
|
||||
|
@@ -38,6 +38,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<option v-for="item in c.items" :key="item.value" :value="item.value">{{ item.text }}</option>
|
||||
</MkSelect>
|
||||
<MkButton v-else-if="c.type === 'postFormButton'" :primary="c.primary" :rounded="c.rounded" :small="size === 'small'" inline @click="openPostForm">{{ c.text }}</MkButton>
|
||||
<div v-else-if="c.type === 'postForm'" :class="$style.postForm">
|
||||
<MkPostForm
|
||||
fixed
|
||||
:instant="true"
|
||||
:initialText="c.form.text"
|
||||
/>
|
||||
</div>
|
||||
<MkFolder v-else-if="c.type === 'folder'" :defaultOpen="c.opened">
|
||||
<template #label>{{ c.title }}</template>
|
||||
<template v-for="child in c.children" :key="child">
|
||||
@@ -62,6 +69,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
|
||||
import MkSelect from '@/components/MkSelect.vue';
|
||||
import { AsUiComponent } from '@/scripts/aiscript/ui';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkPostForm from '@/components/MkPostForm.vue';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
component: AsUiComponent;
|
||||
@@ -114,4 +122,9 @@ function openPostForm() {
|
||||
.fontMonospace {
|
||||
font-family: Fira code, Fira Mono, Consolas, Menlo, Courier, monospace;
|
||||
}
|
||||
|
||||
.postForm {
|
||||
background: var(--bg);
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -14,8 +14,8 @@ 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';
|
||||
import { UserLite } from 'misskey-js/built/entities';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
userIds: string[];
|
||||
@@ -24,11 +24,11 @@ const props = withDefaults(defineProps<{
|
||||
limit: Infinity,
|
||||
});
|
||||
|
||||
const users = ref<UserLite[]>([]);
|
||||
const users = ref<Misskey.entities.UserLite[]>([]);
|
||||
|
||||
onMounted(async () => {
|
||||
users.value = await os.api('users/show', {
|
||||
userIds: props.userIds,
|
||||
}) as unknown as UserLite[];
|
||||
}) as unknown as Misskey.entities.UserLite[];
|
||||
});
|
||||
</script>
|
||||
|
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import Cropper from 'cropperjs';
|
||||
import tinycolor from 'tinycolor2';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
@@ -44,13 +44,13 @@ import { i18n } from '@/i18n';
|
||||
import { getProxiedImageUrl } from '@/scripts/media-proxy';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', cropped: misskey.entities.DriveFile): void;
|
||||
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
file: misskey.entities.DriveFile;
|
||||
file: Misskey.entities.DriveFile;
|
||||
aspectRatio: number;
|
||||
uploadFolder?: string | null;
|
||||
}>();
|
||||
@@ -62,7 +62,7 @@ let cropper: Cropper | null = null;
|
||||
let loading = $ref(true);
|
||||
|
||||
const ok = async () => {
|
||||
const promise = new Promise<misskey.entities.DriveFile>(async (res) => {
|
||||
const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
|
||||
const croppedCanvas = await cropper?.getCropperSelection()?.$toCanvas();
|
||||
croppedCanvas?.toBlob(blob => {
|
||||
if (!blob) return;
|
||||
|
@@ -12,13 +12,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { concat } from '@/scripts/array';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: boolean;
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
|
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkA
|
||||
v-for="file in items"
|
||||
:key="file.id"
|
||||
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Acct.toString(file.user) : 'system'}`"
|
||||
v-tooltip.mfm="`${file.type}\n${bytes(file.size)}\n${dateString(file.createdAt)}\nby ${file.user ? '@' + Misskey.acct.toString(file.user) : 'system'}`"
|
||||
:to="`/admin/file/${file.id}`"
|
||||
class="file _button"
|
||||
>
|
||||
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
|
@@ -23,7 +23,7 @@ import { } from 'vue';
|
||||
import { userName } from '@/filters/user';
|
||||
|
||||
const props = defineProps<{
|
||||
//flash: misskey.entities.Flash;
|
||||
//flash: Misskey.entities.Flash;
|
||||
flash: any;
|
||||
}>();
|
||||
</script>
|
||||
|
@@ -32,13 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { computed, ref } from 'vue';
|
||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
const props = defineProps<{
|
||||
post: misskey.entities.GalleryPost;
|
||||
post: Misskey.entities.GalleryPost;
|
||||
}>();
|
||||
|
||||
const hover = ref(false);
|
||||
|
@@ -15,13 +15,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os';
|
||||
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
|
||||
|
||||
const props = defineProps<{
|
||||
instance: misskey.entities.Instance;
|
||||
instance: Misskey.entities.Instance;
|
||||
}>();
|
||||
|
||||
let chartValues = $ref<number[] | null>(null);
|
||||
|
@@ -59,7 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
@@ -67,7 +67,7 @@ import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
|
||||
const props = defineProps<{
|
||||
invite: misskey.entities.Invite;
|
||||
invite: Misskey.entities.Invite;
|
||||
moderator?: boolean;
|
||||
}>();
|
||||
|
||||
|
@@ -34,12 +34,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { soundConfigStore } from '@/scripts/sound';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
media: misskey.entities.DriveFile;
|
||||
media: Misskey.entities.DriveFile;
|
||||
}>(), {
|
||||
});
|
||||
|
||||
|
@@ -5,33 +5,40 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<template>
|
||||
<div :class="hide ? $style.hidden : $style.visible" :style="darkMode ? '--c: rgb(255 255 255 / 2%);' : '--c: rgb(0 0 0 / 2%);'" @click="onclick">
|
||||
<a
|
||||
:class="$style.imageContainer"
|
||||
:href="image.url"
|
||||
:title="image.name"
|
||||
<component
|
||||
:is="disableImageLink ? 'div' : 'a'"
|
||||
v-bind="disableImageLink ? {
|
||||
title: image.name,
|
||||
class: $style.imageContainer,
|
||||
} : {
|
||||
title: image.name,
|
||||
class: $style.imageContainer,
|
||||
href: image.url,
|
||||
style: 'cursor: zoom-in;'
|
||||
}"
|
||||
>
|
||||
<ImgWithBlurhash
|
||||
:hash="image.blurhash"
|
||||
:src="(defaultStore.state.enableDataSaverMode && hide) ? null : url"
|
||||
:forceBlurhash="hide"
|
||||
:cover="hide"
|
||||
:cover="hide || cover"
|
||||
:alt="image.comment || image.name"
|
||||
:title="image.comment || image.name"
|
||||
:width="image.properties.width"
|
||||
:height="image.properties.height"
|
||||
:style="hide ? 'filter: brightness(0.5);' : null"
|
||||
/>
|
||||
</a>
|
||||
</component>
|
||||
<template v-if="hide">
|
||||
<div :class="$style.hiddenText">
|
||||
<div :class="$style.hiddenTextWrapper">
|
||||
<b v-if="image.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ defaultStore.state.enableDataSaverMode ? ` (${i18n.ts.image}${image.size ? ' ' + bytes(image.size) : ''})` : '' }}</b>
|
||||
<b v-else style="display: block;"><i class="ti ti-photo"></i> {{ defaultStore.state.enableDataSaverMode && image.size ? bytes(image.size) : i18n.ts.image }}</b>
|
||||
<span style="display: block;">{{ i18n.ts.clickToShow }}</span>
|
||||
<span v-if="controls" style="display: block;">{{ i18n.ts.clickToShow }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-else-if="controls">
|
||||
<div :class="$style.indicators">
|
||||
<div v-if="['image/gif', 'image/apng'].includes(image.type)" :class="$style.indicator">GIF</div>
|
||||
<div v-if="image.comment" :class="$style.indicator">ALT</div>
|
||||
@@ -45,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { getStaticImageUrl } from '@/scripts/media-proxy';
|
||||
import bytes from '@/filters/bytes';
|
||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||
@@ -54,10 +61,17 @@ import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
import { iAmModerator } from '@/account';
|
||||
|
||||
const props = defineProps<{
|
||||
image: misskey.entities.DriveFile;
|
||||
const props = withDefaults(defineProps<{
|
||||
image: Misskey.entities.DriveFile;
|
||||
raw?: boolean;
|
||||
}>();
|
||||
cover?: boolean;
|
||||
disableImageLink?: boolean;
|
||||
controls?: boolean;
|
||||
}>(), {
|
||||
cover: false,
|
||||
disableImageLink: false,
|
||||
controls: true,
|
||||
});
|
||||
|
||||
let hide = $ref(true);
|
||||
let darkMode: boolean = $ref(defaultStore.state.darkMode);
|
||||
@@ -70,6 +84,9 @@ const url = $computed(() => (props.raw || defaultStore.state.loadRawImages)
|
||||
);
|
||||
|
||||
function onclick() {
|
||||
if (!props.controls) {
|
||||
return;
|
||||
}
|
||||
if (hide) {
|
||||
hide = false;
|
||||
}
|
||||
@@ -117,6 +134,7 @@ function showMenu(ev: MouseEvent) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.hide {
|
||||
@@ -167,7 +185,6 @@ function showMenu(ev: MouseEvent) {
|
||||
|
||||
.imageContainer {
|
||||
display: block;
|
||||
cursor: zoom-in;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
@@ -64,7 +64,7 @@ async function getClientWidthWithCache(targetEl: HTMLElement, containerEl: HTMLE
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, shallowRef } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import PhotoSwipeLightbox from 'photoswipe/lightbox';
|
||||
import PhotoSwipe from 'photoswipe';
|
||||
import 'photoswipe/style.css';
|
||||
@@ -77,7 +77,7 @@ import { defaultStore } from '@/store';
|
||||
import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll';
|
||||
|
||||
const props = defineProps<{
|
||||
mediaList: misskey.entities.DriveFile[];
|
||||
mediaList: Misskey.entities.DriveFile[];
|
||||
raw?: boolean;
|
||||
}>();
|
||||
|
||||
@@ -252,7 +252,7 @@ onUnmounted(() => {
|
||||
lightbox = null;
|
||||
});
|
||||
|
||||
const previewable = (file: misskey.entities.DriveFile): boolean => {
|
||||
const previewable = (file: Misskey.entities.DriveFile): boolean => {
|
||||
if (file.type === 'image/svg+xml') return true; // svgのwebpublic/thumbnailはpngなのでtrue
|
||||
// FILE_TYPE_BROWSERSAFEに適合しないものはブラウザで表示するのに不適切
|
||||
return (file.type.startsWith('video') || file.type.startsWith('image')) && FILE_TYPE_BROWSERSAFE.includes(file.type);
|
||||
|
@@ -32,13 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import bytes from '@/filters/bytes';
|
||||
import { defaultStore } from '@/store';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
video: misskey.entities.DriveFile;
|
||||
video: Misskey.entities.DriveFile;
|
||||
}>();
|
||||
|
||||
const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.enableDataSaverMode) ? true : (props.video.isSensitive && defaultStore.state.nsfw !== 'ignore'));
|
||||
|
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</I18n>
|
||||
<div :class="$style.renoteInfo">
|
||||
<button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()">
|
||||
<i v-if="isMyRenote" class="ti ti-dots" :class="$style.renoteMenu"></i>
|
||||
<i class="ti ti-dots" :class="$style.renoteMenu"></i>
|
||||
<MkTime :time="note.createdAt"/>
|
||||
</button>
|
||||
<span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]">
|
||||
@@ -140,7 +140,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent } from 'vue';
|
||||
import * as mfm from 'mfm-js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
@@ -161,7 +161,7 @@ import { reactionPicker } from '@/scripts/reaction-picker';
|
||||
import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
|
||||
import { $i } from '@/account';
|
||||
import { i18n } from '@/i18n';
|
||||
import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
|
||||
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
|
||||
import { useNoteCapture } from '@/scripts/use-note-capture';
|
||||
import { deepClone } from '@/scripts/clone';
|
||||
import { useTooltip } from '@/scripts/use-tooltip';
|
||||
@@ -173,12 +173,12 @@ import { showMovedDialog } from '@/scripts/show-moved-dialog';
|
||||
import { shouldCollapsed } from '@/scripts/collapsed';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
pinned?: boolean;
|
||||
}>();
|
||||
|
||||
const inChannel = inject('inChannel', null);
|
||||
const currentClip = inject<Ref<misskey.entities.Clip> | null>('currentClip', null);
|
||||
const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);
|
||||
|
||||
let note = $ref(deepClone(props.note));
|
||||
|
||||
@@ -206,7 +206,7 @@ const renoteButton = shallowRef<HTMLElement>();
|
||||
const renoteTime = shallowRef<HTMLElement>();
|
||||
const reactButton = shallowRef<HTMLElement>();
|
||||
const clipButton = shallowRef<HTMLElement>();
|
||||
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note);
|
||||
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
|
||||
const isMyRenote = $i && ($i.id === note.userId);
|
||||
const showContent = ref(false);
|
||||
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
|
||||
@@ -319,9 +319,15 @@ function renote(viaKeyboard = false) {
|
||||
const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility;
|
||||
const localOnly = defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly;
|
||||
|
||||
let visibility = appearNote.visibility;
|
||||
visibility = smallerVisibility(visibility, configuredVisibility);
|
||||
if (appearNote.channel?.isSensitive) {
|
||||
visibility = smallerVisibility(visibility, 'home');
|
||||
}
|
||||
|
||||
os.api('notes/create', {
|
||||
localOnly,
|
||||
visibility: smallerVisibility(appearNote.visibility, configuredVisibility),
|
||||
visibility,
|
||||
renoteId: appearNote.id,
|
||||
}).then(() => {
|
||||
os.toast(i18n.ts.renoted);
|
||||
@@ -425,21 +431,39 @@ async function clip() {
|
||||
}
|
||||
|
||||
function showRenoteMenu(viaKeyboard = false): void {
|
||||
if (!isMyRenote) return;
|
||||
pleaseLogin();
|
||||
os.popupMenu([{
|
||||
text: i18n.ts.unrenote,
|
||||
icon: 'ti ti-trash',
|
||||
danger: true,
|
||||
action: () => {
|
||||
os.api('notes/delete', {
|
||||
noteId: note.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
}], renoteTime.value, {
|
||||
viaKeyboard: viaKeyboard,
|
||||
});
|
||||
function getUnrenote(): MenuItem {
|
||||
return {
|
||||
text: i18n.ts.unrenote,
|
||||
icon: 'ti ti-trash',
|
||||
danger: true,
|
||||
action: () => {
|
||||
os.api('notes/delete', {
|
||||
noteId: note.id,
|
||||
});
|
||||
isDeleted.value = true;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isMyRenote) {
|
||||
pleaseLogin();
|
||||
os.popupMenu([
|
||||
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
|
||||
null,
|
||||
getUnrenote(),
|
||||
], renoteTime.value, {
|
||||
viaKeyboard: viaKeyboard,
|
||||
});
|
||||
} else {
|
||||
os.popupMenu([
|
||||
getCopyNoteLinkMenu(note, i18n.ts.copyLinkRenote),
|
||||
null,
|
||||
getAbuseNoteMenu(note, i18n.ts.reportAbuseRenote),
|
||||
$i.isModerator || $i.isAdmin ? getUnrenote() : undefined,
|
||||
], renoteTime.value, {
|
||||
viaKeyboard: viaKeyboard,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function focus() {
|
||||
|
@@ -141,7 +141,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { computed, inject, onMounted, ref, shallowRef } from 'vue';
|
||||
import * as mfm from 'mfm-js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteSub from '@/components/MkNoteSub.vue';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
|
||||
@@ -171,7 +171,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue';
|
||||
import { showMovedDialog } from '@/scripts/show-moved-dialog';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const inChannel = inject('inChannel', null);
|
||||
@@ -202,7 +202,7 @@ const renoteButton = shallowRef<HTMLElement>();
|
||||
const renoteTime = shallowRef<HTMLElement>();
|
||||
const reactButton = shallowRef<HTMLElement>();
|
||||
const clipButton = shallowRef<HTMLElement>();
|
||||
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note);
|
||||
let appearNote = $computed(() => isRenote ? note.renote as Misskey.entities.Note : note);
|
||||
const isMyRenote = $i && ($i.id === note.userId);
|
||||
const showContent = ref(false);
|
||||
const isDeleted = ref(false);
|
||||
@@ -211,8 +211,8 @@ const translation = ref(null);
|
||||
const translating = ref(false);
|
||||
const urls = appearNote.text ? extractUrlFromMfm(mfm.parse(appearNote.text)) : null;
|
||||
const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance);
|
||||
const conversation = ref<misskey.entities.Note[]>([]);
|
||||
const replies = ref<misskey.entities.Note[]>([]);
|
||||
const conversation = ref<Misskey.entities.Note[]>([]);
|
||||
const replies = ref<Misskey.entities.Note[]>([]);
|
||||
const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || appearNote.userId === $i.id);
|
||||
|
||||
const keymap = {
|
||||
|
@@ -30,13 +30,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
import { notePage } from '@/filters/note';
|
||||
import { userPage } from '@/filters/user';
|
||||
|
||||
defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
@@ -23,14 +23,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
import MkCwButton from '@/components/MkCwButton.vue';
|
||||
import { $i } from '@/account';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const showContent = $ref(false);
|
||||
|
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteHeader from '@/components/MkNoteHeader.vue';
|
||||
import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
|
||||
import MkCwButton from '@/components/MkCwButton.vue';
|
||||
@@ -54,7 +54,7 @@ import { checkWordMute } from "@/scripts/check-word-mute";
|
||||
import { defaultStore } from "@/store";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
detail?: boolean;
|
||||
|
||||
// how many notes are in between this one and the note being viewed in detail
|
||||
@@ -66,7 +66,7 @@ const props = withDefaults(defineProps<{
|
||||
const muted = ref(checkWordMute(props.note, $i, defaultStore.state.mutedWords));
|
||||
|
||||
let showContent = $ref(false);
|
||||
let replies: misskey.entities.Note[] = $ref([]);
|
||||
let replies: Misskey.entities.Note[] = $ref([]);
|
||||
|
||||
if (props.detail) {
|
||||
os.api('notes/children', {
|
||||
|
@@ -8,6 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div :class="$style.head">
|
||||
<MkAvatar v-if="notification.type === 'pollEnded'" :class="$style.icon" :user="notification.note.user" link preview/>
|
||||
<MkAvatar v-else-if="notification.type === 'achievementEarned'" :class="$style.icon" :user="$i" link preview/>
|
||||
<img v-else-if="notification.type === 'test'" :class="$style.icon" :src="infoImageUrl"/>
|
||||
<MkAvatar v-else-if="notification.user" :class="$style.icon" :user="notification.user" link preview/>
|
||||
<img v-else-if="notification.icon" :class="$style.icon" :src="notification.icon" alt=""/>
|
||||
<div
|
||||
@@ -47,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<header :class="$style.header">
|
||||
<span v-if="notification.type === 'pollEnded'">{{ i18n.ts._notification.pollEnded }}</span>
|
||||
<span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span>
|
||||
<span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span>
|
||||
<MkA v-else-if="notification.user" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA>
|
||||
<span v-else>{{ notification.header }}</span>
|
||||
<MkTime v-if="withTime" :time="notification.createdAt" :class="$style.headerTime"/>
|
||||
@@ -91,6 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkButton :class="$style.followRequestCommandButton" rounded danger @click="rejectFollowRequest()"><i class="ti ti-x"/> {{ i18n.ts.reject }}</MkButton>
|
||||
</div>
|
||||
</template>
|
||||
<span v-else-if="notification.type === 'test'" :class="$style.text">{{ i18n.ts._notification.notificationWillBeDisplayedLikeThis }}</span>
|
||||
<span v-else-if="notification.type === 'app'" :class="$style.text">
|
||||
<Mfm :text="notification.body" :nowrap="false"/>
|
||||
</span>
|
||||
@@ -101,7 +104,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, shallowRef } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
import XReactionTooltip from '@/components/MkReactionTooltip.vue';
|
||||
@@ -113,9 +116,10 @@ import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
import { useTooltip } from '@/scripts/use-tooltip';
|
||||
import { $i } from '@/account';
|
||||
import { infoImageUrl } from '@/instance';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
notification: misskey.entities.Notification;
|
||||
notification: Misskey.entities.Notification;
|
||||
withTime?: boolean;
|
||||
full?: boolean;
|
||||
}>(), {
|
||||
|
@@ -5,7 +5,15 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<template>
|
||||
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj" tabindex="-1">
|
||||
<div v-if="page.eyeCatchingImage" class="thumbnail" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
|
||||
<div v-if="page.eyeCatchingImage" class="thumbnail">
|
||||
<MediaImage
|
||||
:image="page.eyeCatchingImage"
|
||||
:disableImageLink="true"
|
||||
:controls="false"
|
||||
:cover="true"
|
||||
:class="$style.eyeCatchingImageRoot"
|
||||
/>
|
||||
</div>
|
||||
<article>
|
||||
<header>
|
||||
<h1 :title="page.title">{{ page.title }}</h1>
|
||||
@@ -21,14 +29,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { userName } from '@/filters/user';
|
||||
import MediaImage from '@/components/MkMediaImage.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
page: misskey.entities.Page;
|
||||
page: Misskey.entities.Page;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
.eyeCatchingImageRoot {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-radius: var(--radius) var(--radius) 0 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vhpxefrj {
|
||||
display: block;
|
||||
@@ -39,32 +57,15 @@ const props = defineProps<{
|
||||
}
|
||||
|
||||
> .thumbnail {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
> button {
|
||||
font-size: 3.5em;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
font-size: 4em;
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
& + article {
|
||||
left: 100px;
|
||||
width: calc(100% - 100px);
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
}
|
||||
}
|
||||
|
||||
> article {
|
||||
background-color: var(--panel);
|
||||
padding: 16px;
|
||||
border-radius: var(--radius);
|
||||
|
||||
> header {
|
||||
margin-bottom: 8px;
|
||||
|
@@ -44,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os';
|
||||
import { isBottomVisible, isTopVisible, getScrollContainer, scrollToBottom, scrollToTop, scrollBy, scroll, getBodyScrollHeight } from '@/scripts/scroll';
|
||||
import { useDocumentVisibility } from '@/scripts/use-document-visibility';
|
||||
@@ -58,7 +58,7 @@ const SECOND_FETCH_LIMIT = 30;
|
||||
const TOLERANCE = 6;
|
||||
const APPEAR_MINIMUM_INTERVAL = 600;
|
||||
|
||||
export type Paging<E extends keyof misskey.Endpoints = keyof misskey.Endpoints> = {
|
||||
export type Paging<E extends keyof Misskey.Endpoints = keyof Misskey.Endpoints> = {
|
||||
endpoint: E;
|
||||
|
||||
/**
|
||||
@@ -71,7 +71,7 @@ export type Paging<E extends keyof misskey.Endpoints = keyof misskey.Endpoints>
|
||||
*/
|
||||
displayLimit?: number;
|
||||
|
||||
params?: misskey.Endpoints[E]['req'] | ComputedRef<misskey.Endpoints[E]['req']>;
|
||||
params?: Misskey.Endpoints[E]['req'] | ComputedRef<Misskey.Endpoints[E]['req']>;
|
||||
|
||||
/**
|
||||
* 検索APIのような、ページング不可なエンドポイントを利用する場合
|
||||
|
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { sum } from '@/scripts/array';
|
||||
import { pleaseLogin } from '@/scripts/please-login';
|
||||
import * as os from '@/os';
|
||||
@@ -36,7 +36,7 @@ import { i18n } from '@/i18n';
|
||||
import { useInterval } from '@/scripts/use-interval';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
readOnly?: boolean;
|
||||
}>();
|
||||
|
||||
|
@@ -100,10 +100,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue';
|
||||
import * as mfm from 'mfm-js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||
import { toASCII } from 'punycode/';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import MkNoteSimple from '@/components/MkNoteSimple.vue';
|
||||
import MkNotePreview from '@/components/MkNotePreview.vue';
|
||||
import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
|
||||
@@ -129,17 +128,17 @@ import { claimAchievement } from '@/scripts/achievements';
|
||||
const modal = inject('modal');
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
reply?: misskey.entities.Note;
|
||||
renote?: misskey.entities.Note;
|
||||
channel?: misskey.entities.Channel; // TODO
|
||||
mention?: misskey.entities.User;
|
||||
specified?: misskey.entities.User;
|
||||
reply?: Misskey.entities.Note;
|
||||
renote?: Misskey.entities.Note;
|
||||
channel?: Misskey.entities.Channel; // TODO
|
||||
mention?: Misskey.entities.User;
|
||||
specified?: Misskey.entities.User;
|
||||
initialText?: string;
|
||||
initialVisibility?: (typeof misskey.noteVisibilities)[number];
|
||||
initialFiles?: misskey.entities.DriveFile[];
|
||||
initialVisibility?: (typeof Misskey.noteVisibilities)[number];
|
||||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: misskey.entities.User[];
|
||||
initialNote?: misskey.entities.Note;
|
||||
initialVisibleUsers?: Misskey.entities.User[];
|
||||
initialNote?: Misskey.entities.Note;
|
||||
instant?: boolean;
|
||||
fixed?: boolean;
|
||||
autofocus?: boolean;
|
||||
@@ -175,7 +174,7 @@ let showPreview = $ref(defaultStore.state.showPreview);
|
||||
watch($$(showPreview), () => defaultStore.set('showPreview', showPreview));
|
||||
let cw = $ref<string | null>(null);
|
||||
let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly);
|
||||
let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof misskey.noteVisibilities[number]);
|
||||
let visibility = $ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]);
|
||||
let visibleUsers = $ref([]);
|
||||
if (props.initialVisibleUsers) {
|
||||
props.initialVisibleUsers.forEach(pushVisibleUser);
|
||||
@@ -416,7 +415,7 @@ function updateFileName(file, name) {
|
||||
files[files.findIndex(x => x.id === file.id)].name = name;
|
||||
}
|
||||
|
||||
function replaceFile(file: misskey.entities.DriveFile, newFile: misskey.entities.DriveFile): void {
|
||||
function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void {
|
||||
files[files.findIndex(x => x.id === file.id)] = newFile;
|
||||
}
|
||||
|
||||
@@ -516,7 +515,7 @@ function addVisibleUser() {
|
||||
pushVisibleUser(user);
|
||||
|
||||
if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) {
|
||||
text = `@${Acct.toString(user)} ${text}`;
|
||||
text = `@${Misskey.acct.toString(user)} ${text}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -630,6 +629,8 @@ function onDrop(ev): void {
|
||||
}
|
||||
|
||||
function saveDraft() {
|
||||
if (props.instant) return;
|
||||
|
||||
const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}');
|
||||
|
||||
draftData[draftKey] = {
|
||||
@@ -761,7 +762,7 @@ async function post(ev?: MouseEvent) {
|
||||
'https://youtu.be/Efrlqw8ytg4',
|
||||
'https://www.youtube.com/watch?v=Efrlqw8ytg4',
|
||||
'https://m.youtube.com/watch?v=Efrlqw8ytg4',
|
||||
|
||||
|
||||
'https://youtu.be/XVCwzwxdHuA',
|
||||
'https://www.youtube.com/watch?v=XVCwzwxdHuA',
|
||||
'https://m.youtube.com/watch?v=XVCwzwxdHuA',
|
||||
@@ -804,7 +805,7 @@ function cancel() {
|
||||
|
||||
function insertMention() {
|
||||
os.selectUser().then(user => {
|
||||
insertTextAtCursor(textareaEl, '@' + Acct.toString(user) + ' ');
|
||||
insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' ');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -825,7 +826,7 @@ function showActions(ev) {
|
||||
})), ev.currentTarget ?? ev.target);
|
||||
}
|
||||
|
||||
let postAccount = $ref<misskey.entities.UserDetailed | null>(null);
|
||||
let postAccount = $ref<Misskey.entities.UserDetailed | null>(null);
|
||||
|
||||
function openAccountMenu(ev: MouseEvent) {
|
||||
openAccountMenu_({
|
||||
|
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
@@ -36,9 +36,9 @@ const props = defineProps<{
|
||||
const emit = defineEmits<{
|
||||
(ev: 'update:modelValue', value: any[]): void;
|
||||
(ev: 'detach', id: string): void;
|
||||
(ev: 'changeSensitive', file: misskey.entities.DriveFile, isSensitive: boolean): void;
|
||||
(ev: 'changeName', file: misskey.entities.DriveFile, newName: string): void;
|
||||
(ev: 'replaceFile', file: misskey.entities.DriveFile, newFile: misskey.entities.DriveFile): void;
|
||||
(ev: 'changeSensitive', file: Misskey.entities.DriveFile, isSensitive: boolean): void;
|
||||
(ev: 'changeName', file: Misskey.entities.DriveFile, newName: string): void;
|
||||
(ev: 'replaceFile', file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void;
|
||||
}>();
|
||||
|
||||
let menuShowing = false;
|
||||
@@ -92,12 +92,12 @@ async function describe(file) {
|
||||
}, 'closed');
|
||||
}
|
||||
|
||||
async function crop(file: misskey.entities.DriveFile): Promise<void> {
|
||||
async function crop(file: Misskey.entities.DriveFile): Promise<void> {
|
||||
const newFile = await os.cropImage(file, { aspectRatio: NaN });
|
||||
emit('replaceFile', file, newFile);
|
||||
}
|
||||
|
||||
function showFileMenu(file: misskey.entities.DriveFile, ev: MouseEvent): void {
|
||||
function showFileMenu(file: Misskey.entities.DriveFile, ev: MouseEvent): void {
|
||||
if (menuShowing) return;
|
||||
|
||||
const isImage = file.type.startsWith('image/');
|
||||
|
@@ -11,22 +11,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkPostForm from '@/components/MkPostForm.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
reply?: misskey.entities.Note;
|
||||
renote?: misskey.entities.Note;
|
||||
reply?: Misskey.entities.Note;
|
||||
renote?: Misskey.entities.Note;
|
||||
channel?: any; // TODO
|
||||
mention?: misskey.entities.User;
|
||||
specified?: misskey.entities.User;
|
||||
mention?: Misskey.entities.User;
|
||||
specified?: Misskey.entities.User;
|
||||
initialText?: string;
|
||||
initialVisibility?: typeof misskey.noteVisibilities;
|
||||
initialFiles?: misskey.entities.DriveFile[];
|
||||
initialVisibility?: typeof Misskey.noteVisibilities;
|
||||
initialFiles?: Misskey.entities.DriveFile[];
|
||||
initialLocalOnly?: boolean;
|
||||
initialVisibleUsers?: misskey.entities.User[];
|
||||
initialNote?: misskey.entities.Note;
|
||||
initialVisibleUsers?: Misskey.entities.User[];
|
||||
initialNote?: Misskey.entities.Note;
|
||||
instant?: boolean;
|
||||
fixed?: boolean;
|
||||
autofocus?: boolean;
|
||||
|
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
@@ -54,12 +54,12 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
noteId: misskey.entities.Note['id'];
|
||||
noteId: Misskey.entities.Note['id'];
|
||||
}>();
|
||||
|
||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
let note = $ref<misskey.entities.Note>();
|
||||
let note = $ref<Misskey.entities.Note>();
|
||||
let tab = $ref<string>();
|
||||
let reactions = $ref<string[]>();
|
||||
let users = $ref();
|
||||
|
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
ref="buttonEl"
|
||||
v-ripple="canToggle"
|
||||
class="_button"
|
||||
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.large]: defaultStore.state.largeNoteReactions }]"
|
||||
:class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]"
|
||||
@click="toggleReaction()"
|
||||
>
|
||||
<MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/>
|
||||
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, onMounted, shallowRef, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XDetails from '@/components/MkReactionsViewer.details.vue';
|
||||
import MkReactionIcon from '@/components/MkReactionIcon.vue';
|
||||
import * as os from '@/os';
|
||||
@@ -33,7 +33,7 @@ const props = defineProps<{
|
||||
reaction: string;
|
||||
count: number;
|
||||
isInitial: boolean;
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const buttonEl = shallowRef<HTMLElement>();
|
||||
@@ -115,10 +115,11 @@ useTooltip(buttonEl, async (showing) => {
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
height: 42px;
|
||||
margin: 2px;
|
||||
padding: 0 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 1.5em;
|
||||
border-radius: 6px;
|
||||
|
||||
&.canToggle {
|
||||
background: var(--buttonBg);
|
||||
@@ -132,14 +133,25 @@ useTooltip(buttonEl, async (showing) => {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&.large {
|
||||
height: 42px;
|
||||
font-size: 1.5em;
|
||||
border-radius: 6px;
|
||||
&.small {
|
||||
height: 32px;
|
||||
font-size: 1em;
|
||||
border-radius: 4px;
|
||||
|
||||
> .count {
|
||||
font-size: 0.7em;
|
||||
line-height: 42px;
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
height: 52px;
|
||||
font-size: 2em;
|
||||
border-radius: 8px;
|
||||
|
||||
> .count {
|
||||
font-size: 0.6em;
|
||||
line-height: 52px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,8 +171,8 @@ useTooltip(buttonEl, async (showing) => {
|
||||
}
|
||||
|
||||
.count {
|
||||
font-size: 0.9em;
|
||||
line-height: 32px;
|
||||
font-size: 0.7em;
|
||||
line-height: 42px;
|
||||
margin: 0 0 0 4px;
|
||||
}
|
||||
</style>
|
||||
|
@@ -18,13 +18,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { watch } from 'vue';
|
||||
import XReaction from '@/components/MkReactionsViewer.reaction.vue';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
maxNumber?: number;
|
||||
}>(), {
|
||||
maxNumber: Infinity,
|
||||
|
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||
import { userPage } from '@/filters/user';
|
||||
@@ -47,12 +47,12 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const props = defineProps<{
|
||||
noteId: misskey.entities.Note['id'];
|
||||
noteId: Misskey.entities.Note['id'];
|
||||
}>();
|
||||
|
||||
const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
|
||||
let note = $ref<misskey.entities.Note>();
|
||||
let note = $ref<Misskey.entities.Note>();
|
||||
let renotes = $ref();
|
||||
let users = $ref();
|
||||
|
||||
|
@@ -6,16 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template>
|
||||
<form :class="{ signing, totpLogin }" @submit.prevent="onSubmit">
|
||||
<div class="_gaps_m">
|
||||
<div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : null, marginBottom: message ? '1.5em' : null }"></div>
|
||||
<div v-show="withAvatar" :class="$style.avatar" :style="{ backgroundImage: user ? `url('${ user.avatarUrl }')` : undefined, marginBottom: message ? '1.5em' : undefined }"></div>
|
||||
<MkInfo v-if="message">
|
||||
{{ message }}
|
||||
</MkInfo>
|
||||
<div v-if="!totpLogin" class="normal-signin _gaps_m">
|
||||
<MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
|
||||
<MkInput v-model="username" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autocomplete="username webauthn" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
|
||||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
</MkInput>
|
||||
<MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password" :withPasswordToggle="true" required data-cy-signin-password>
|
||||
<MkInput v-if="!user || user && !user.usePasswordLessLogin" v-model="password" :placeholder="i18n.ts.password" type="password" autocomplete="current-password webauthn" :withPasswordToggle="true" required data-cy-signin-password>
|
||||
<template #prefix><i class="ti ti-lock"></i></template>
|
||||
<template #caption><button class="_textButton" type="button" @click="resetPassword">{{ i18n.ts.forgotPassword }}</button></template>
|
||||
</MkInput>
|
||||
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
<div v-if="totpLogin" class="2fa-signin" :class="{ securityKeys: user && user.securityKeys }">
|
||||
<div v-if="user && user.securityKeys" class="twofa-group tap-group">
|
||||
<p>{{ i18n.ts.tapSecurityKey }}</p>
|
||||
<p>{{ i18n.ts.useSecurityKey }}</p>
|
||||
<MkButton v-if="!queryingKey" @click="queryKey">
|
||||
{{ i18n.ts.retry }}
|
||||
</MkButton>
|
||||
@@ -32,12 +32,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<p class="or-msg">{{ i18n.ts.or }}</p>
|
||||
</div>
|
||||
<div class="twofa-group totp-group">
|
||||
<p style="margin-bottom:0;">{{ i18n.ts.twoStepAuthentication }}</p>
|
||||
<p style="margin-bottom:0;">{{ i18n.ts['2fa'] }}</p>
|
||||
<MkInput v-if="user && user.usePasswordLessLogin" v-model="password" type="password" autocomplete="current-password" :withPasswordToggle="true" required>
|
||||
<template #label>{{ i18n.ts.password }}</template>
|
||||
<template #prefix><i class="ti ti-lock"></i></template>
|
||||
</MkInput>
|
||||
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="one-time-code" :spellcheck="false" required>
|
||||
<MkInput v-model="token" type="text" pattern="^([0-9]{6}|[A-Z0-9]{32})$" autocomplete="one-time-code" :spellcheck="false" required>
|
||||
<template #label>{{ i18n.ts.token }}</template>
|
||||
<template #prefix><i class="ti ti-123"></i></template>
|
||||
</MkInput>
|
||||
@@ -51,32 +51,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import { showSuspendedDialog } from '../scripts/show-suspended-dialog';
|
||||
import { UserDetailed } from 'misskey-js/built/entities';
|
||||
import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
|
||||
import { showSuspendedDialog } from '@/scripts/show-suspended-dialog';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import { host as configHost } from '@/config';
|
||||
import { byteify, hexify } from '@/scripts/2fa';
|
||||
import * as os from '@/os';
|
||||
import { login } from '@/account';
|
||||
import { instance } from '@/instance';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
let signing = $ref(false);
|
||||
let user = $ref(null);
|
||||
let user = $ref<UserDetailed | null>(null);
|
||||
let username = $ref('');
|
||||
let password = $ref('');
|
||||
let token = $ref('');
|
||||
let host = $ref(toUnicode(configHost));
|
||||
let totpLogin = $ref(false);
|
||||
let credential = $ref(null);
|
||||
let challengeData = $ref(null);
|
||||
let queryingKey = $ref(false);
|
||||
let credentialRequest = $ref<CredentialRequestOptions | null>(null);
|
||||
let hCaptchaResponse = $ref(null);
|
||||
let reCaptchaResponse = $ref(null);
|
||||
|
||||
const meta = $computed(() => instance);
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'login', v: any): void;
|
||||
}>();
|
||||
@@ -99,7 +96,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
function onUsernameChange() {
|
||||
function onUsernameChange(): void {
|
||||
os.api('users/show', {
|
||||
username: username,
|
||||
}).then(userResponse => {
|
||||
@@ -109,58 +106,46 @@ function onUsernameChange() {
|
||||
});
|
||||
}
|
||||
|
||||
function onLogin(res) {
|
||||
function onLogin(res: any): Promise<void> | void {
|
||||
if (props.autoSet) {
|
||||
return login(res.i);
|
||||
}
|
||||
}
|
||||
|
||||
function queryKey() {
|
||||
async function queryKey(): Promise<void> {
|
||||
queryingKey = true;
|
||||
return navigator.credentials.get({
|
||||
publicKey: {
|
||||
challenge: byteify(challengeData.challenge, 'base64'),
|
||||
allowCredentials: challengeData.securityKeys.map(key => ({
|
||||
id: byteify(key.id, 'hex'),
|
||||
type: 'public-key',
|
||||
transports: ['usb', 'nfc', 'ble', 'internal'],
|
||||
})),
|
||||
timeout: 60 * 1000,
|
||||
},
|
||||
}).catch(() => {
|
||||
queryingKey = false;
|
||||
return Promise.reject(null);
|
||||
}).then(credential => {
|
||||
queryingKey = false;
|
||||
signing = true;
|
||||
return os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
signature: hexify(credential.response.signature),
|
||||
authenticatorData: hexify(credential.response.authenticatorData),
|
||||
clientDataJSON: hexify(credential.response.clientDataJSON),
|
||||
credentialId: credential.id,
|
||||
challengeId: challengeData.challengeId,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
await webAuthnRequest(credentialRequest)
|
||||
.catch(() => {
|
||||
queryingKey = false;
|
||||
return Promise.reject(null);
|
||||
}).then(credential => {
|
||||
credentialRequest = null;
|
||||
queryingKey = false;
|
||||
signing = true;
|
||||
return os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
credential: credential.toJSON(),
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
});
|
||||
}).then(res => {
|
||||
emit('login', res);
|
||||
return onLogin(res);
|
||||
}).catch(err => {
|
||||
if (err === null) return;
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: i18n.ts.signinFailed,
|
||||
});
|
||||
signing = false;
|
||||
});
|
||||
}).then(res => {
|
||||
emit('login', res);
|
||||
return onLogin(res);
|
||||
}).catch(err => {
|
||||
if (err === null) return;
|
||||
os.alert({
|
||||
type: 'error',
|
||||
text: i18n.ts.signinFailed,
|
||||
});
|
||||
signing = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onSubmit() {
|
||||
function onSubmit(): void {
|
||||
signing = true;
|
||||
if (!totpLogin && user && user.twoFactorEnabled) {
|
||||
if (window.PublicKeyCredential && user.securityKeys) {
|
||||
if (webAuthnSupported() && user.securityKeys) {
|
||||
os.api('signin', {
|
||||
username,
|
||||
password,
|
||||
@@ -169,9 +154,12 @@ function onSubmit() {
|
||||
}).then(res => {
|
||||
totpLogin = true;
|
||||
signing = false;
|
||||
challengeData = res;
|
||||
return queryKey();
|
||||
}).catch(loginFailed);
|
||||
credentialRequest = parseRequestOptionsFromJSON({
|
||||
publicKey: res,
|
||||
});
|
||||
})
|
||||
.then(() => queryKey())
|
||||
.catch(loginFailed);
|
||||
} else {
|
||||
totpLogin = true;
|
||||
signing = false;
|
||||
@@ -182,7 +170,7 @@ function onSubmit() {
|
||||
password,
|
||||
'hcaptcha-response': hCaptchaResponse,
|
||||
'g-recaptcha-response': reCaptchaResponse,
|
||||
token: user && user.twoFactorEnabled ? token : undefined,
|
||||
token: user?.twoFactorEnabled ? token : undefined,
|
||||
}).then(res => {
|
||||
emit('login', res);
|
||||
onLogin(res);
|
||||
@@ -190,7 +178,7 @@ function onSubmit() {
|
||||
}
|
||||
}
|
||||
|
||||
function loginFailed(err) {
|
||||
function loginFailed(err: any): void {
|
||||
switch (err.id) {
|
||||
case '6cc579cc-885d-43d8-95c2-b8c7fc963280': {
|
||||
os.alert({
|
||||
@@ -221,7 +209,7 @@ function loginFailed(err) {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.log(err);
|
||||
console.error(err);
|
||||
os.alert({
|
||||
type: 'error',
|
||||
title: i18n.ts.loginFailed,
|
||||
@@ -230,12 +218,11 @@ function loginFailed(err) {
|
||||
}
|
||||
}
|
||||
|
||||
challengeData = null;
|
||||
totpLogin = false;
|
||||
signing = false;
|
||||
}
|
||||
|
||||
function resetPassword() {
|
||||
function resetPassword(): void {
|
||||
os.popup(defineAsyncComponent(() => import('@/components/MkForgotPassword.vue')), {}, {
|
||||
}, 'closed');
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkMediaList from '@/components/MkMediaList.vue';
|
||||
import MkPoll from '@/components/MkPoll.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
@@ -39,7 +39,7 @@ import { $i } from '@/account';
|
||||
import { shouldCollapsed } from '@/scripts/collapsed';
|
||||
|
||||
const props = defineProps<{
|
||||
note: misskey.entities.Note;
|
||||
note: Misskey.entities.Note;
|
||||
}>();
|
||||
|
||||
const isLong = shouldCollapsed(props.note);
|
||||
|
@@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { permissions as kinds } from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkInput from './MkInput.vue';
|
||||
import MkSwitch from './MkSwitch.vue';
|
||||
import MkButton from './MkButton.vue';
|
||||
@@ -74,7 +74,7 @@ if (props.initialPermissions) {
|
||||
permissions[kind] = true;
|
||||
}
|
||||
} else {
|
||||
for (const kind of kinds) {
|
||||
for (const kind of Misskey.permissions) {
|
||||
permissions[kind] = false;
|
||||
}
|
||||
}
|
||||
|
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
<div v-else>
|
||||
<component :is="self ? 'MkA' : 'a'" :class="[$style.link, { [$style.compact]: compact }]" :[attr]="self ? url.substring(local.length) : url" rel="nofollow noopener" :target="target" :title="url">
|
||||
<div v-if="thumbnail" :class="$style.thumbnail" :style="`background-image: url('${thumbnail}')`">
|
||||
<div v-if="thumbnail" :class="$style.thumbnail" :style="defaultStore.state.enableDataSaverMode ? '' : `background-image: url('${thumbnail}')`">
|
||||
</div>
|
||||
<article :class="$style.body">
|
||||
<header :class="$style.header">
|
||||
@@ -260,6 +260,7 @@ onUnmounted(() => {
|
||||
height: 100%;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-color: var(--bg);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
@@ -62,7 +62,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkRadios from '@/components/MkRadios.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User,
|
||||
user: Misskey.entities.User,
|
||||
announcement?: any,
|
||||
}>();
|
||||
|
||||
|
@@ -15,14 +15,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { onMounted } from 'vue';
|
||||
import MkMiniChart from '@/components/MkMiniChart.vue';
|
||||
import * as os from '@/os';
|
||||
import { acct } from '@/filters/user';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
withChart: boolean;
|
||||
}>(), {
|
||||
withChart: true,
|
||||
|
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
import number from '@/filters/number';
|
||||
import { userPage } from '@/filters/user';
|
||||
@@ -43,7 +43,7 @@ import { $i } from '@/account';
|
||||
import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe';
|
||||
|
||||
defineProps<{
|
||||
user: misskey.entities.UserDetailed;
|
||||
user: Misskey.entities.UserDetailed;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
|
@@ -17,11 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const text = $computed(() => {
|
||||
|
@@ -56,8 +56,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
@@ -81,7 +80,7 @@ const emit = defineEmits<{
|
||||
}>();
|
||||
|
||||
const zIndex = os.claimZIndex('middle');
|
||||
let user = $ref<misskey.entities.UserDetailed | null>(null);
|
||||
let user = $ref<Misskey.entities.UserDetailed | null>(null);
|
||||
let top = $ref(0);
|
||||
let left = $ref(0);
|
||||
|
||||
@@ -95,7 +94,7 @@ onMounted(() => {
|
||||
user = props.q;
|
||||
} else {
|
||||
const query = props.q.startsWith('@') ?
|
||||
Acct.parse(props.q.substring(1)) :
|
||||
Misskey.acct.parse(props.q.substring(1)) :
|
||||
{ userId: props.q };
|
||||
|
||||
os.api('users/show', query).then(res => {
|
||||
|
@@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
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';
|
||||
@@ -69,7 +69,7 @@ import { $i } from '@/account';
|
||||
import { hostname } from '@/config';
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok', selected: misskey.entities.UserDetailed): void;
|
||||
(ev: 'ok', selected: Misskey.entities.UserDetailed): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
@@ -80,9 +80,9 @@ const props = defineProps<{
|
||||
|
||||
let username = $ref('');
|
||||
let host = $ref('');
|
||||
let users: misskey.entities.UserDetailed[] = $ref([]);
|
||||
let recentUsers: misskey.entities.UserDetailed[] = $ref([]);
|
||||
let selected: misskey.entities.UserDetailed | null = $ref(null);
|
||||
let users: Misskey.entities.UserDetailed[] = $ref([]);
|
||||
let recentUsers: Misskey.entities.UserDetailed[] = $ref([]);
|
||||
let selected: Misskey.entities.UserDetailed | null = $ref(null);
|
||||
let dialogEl = $ref();
|
||||
|
||||
const search = () => {
|
||||
|
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
@@ -33,7 +33,7 @@ import { $i } from '@/account';
|
||||
import * as os from '@/os';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.UserDetailed;
|
||||
user: Misskey.entities.UserDetailed;
|
||||
}>();
|
||||
|
||||
const isFollowing = ref(false);
|
||||
|
@@ -43,27 +43,27 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { nextTick } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const modal = $shallowRef<InstanceType<typeof MkModal>>();
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
currentVisibility: typeof misskey.noteVisibilities[number];
|
||||
currentVisibility: typeof Misskey.noteVisibilities[number];
|
||||
localOnly: boolean;
|
||||
src?: HTMLElement;
|
||||
}>(), {
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'changeVisibility', v: typeof misskey.noteVisibilities[number]): void;
|
||||
(ev: 'changeVisibility', v: typeof Misskey.noteVisibilities[number]): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
let v = $ref(props.currentVisibility);
|
||||
|
||||
function choose(visibility: typeof misskey.noteVisibilities[number]): void {
|
||||
function choose(visibility: typeof Misskey.noteVisibilities[number]): void {
|
||||
v = visibility;
|
||||
emit('changeVisibility', visibility);
|
||||
nextTick(() => {
|
||||
|
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { Instance } from 'misskey-js/built/entities';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XTimeline from './welcome.timeline.vue';
|
||||
import XSigninDialog from '@/components/MkSigninDialog.vue';
|
||||
import XSignupDialog from '@/components/MkSignupDialog.vue';
|
||||
@@ -65,7 +65,7 @@ import number from '@/filters/number';
|
||||
import MkNumber from '@/components/MkNumber.vue';
|
||||
import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
|
||||
|
||||
let meta = $ref<Instance>();
|
||||
let meta = $ref<Misskey.entities.Instance>();
|
||||
let stats = $ref(null);
|
||||
|
||||
os.api('meta', { detail: true }).then(_meta => {
|
||||
|
@@ -15,14 +15,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import MkCondensedLine from './MkCondensedLine.vue';
|
||||
import { host as hostRaw } from '@/config';
|
||||
import { defaultStore } from '@/store';
|
||||
|
||||
defineProps<{
|
||||
user: misskey.entities.UserDetailed;
|
||||
user: Misskey.entities.UserDetailed;
|
||||
detail?: boolean;
|
||||
}>();
|
||||
|
||||
|
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
|
||||
import MkA from './MkA.vue';
|
||||
import { getStaticImageUrl } from '@/scripts/media-proxy';
|
||||
@@ -42,7 +42,7 @@ const squareAvatars = $ref(defaultStore.state.squareAvatars);
|
||||
const useBlurEffect = $ref(defaultStore.state.useBlurEffect);
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
target?: string | null;
|
||||
link?: boolean;
|
||||
preview?: boolean;
|
||||
|
@@ -9,10 +9,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
nowrap?: boolean;
|
||||
}>(), {
|
||||
nowrap: true,
|
||||
|
@@ -5,20 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<ImgWithBlurhash v-if="image" style="max-width: 100%;" :hash="image.blurhash" :src="image.url" :alt="image.comment" :title="image.comment" :width="image.properties.width" :height="image.properties.height" :cover="false"/>
|
||||
<MediaImage
|
||||
v-if="image"
|
||||
:image="image"
|
||||
:disableImageLink="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { ref } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { ImageBlock } from './block.type';
|
||||
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
|
||||
import MediaImage from '@/components/MkMediaImage.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
block: ImageBlock,
|
||||
page: Misskey.entities.Page,
|
||||
}>();
|
||||
|
||||
const image = props.page.attachedFiles.find(x => x.id === props.block.fileId);
|
||||
const image = ref<Misskey.entities.DriveFile>(props.page.attachedFiles.find(x => x.id === props.block.fileId));
|
||||
</script>
|
||||
|
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }">
|
||||
<div :class="{ [$style.center]: page.alignCenter, [$style.serif]: page.font === 'serif' }" class="_gaps_s">
|
||||
<XBlock v-for="child in page.content" :key="child.id" :page="page" :block="child" :h="2"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@@ -3,15 +3,15 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import { url } from '@/config';
|
||||
|
||||
export const acct = (user: misskey.Acct) => {
|
||||
return Acct.toString(user);
|
||||
return Misskey.acct.toString(user);
|
||||
};
|
||||
|
||||
export const userName = (user: misskey.entities.User) => {
|
||||
export const userName = (user: Misskey.entities.User) => {
|
||||
return user.name || user.username;
|
||||
};
|
||||
|
||||
|
@@ -41,4 +41,12 @@ export const miLocalStorage = {
|
||||
getItem: (key: Keys): string | null => window.localStorage.getItem(key),
|
||||
setItem: (key: Keys, value: string): void => window.localStorage.setItem(key, value),
|
||||
removeItem: (key: Keys): void => window.localStorage.removeItem(key),
|
||||
getItemAsJson: (key: Keys): any | undefined => {
|
||||
const item = miLocalStorage.getItem(key);
|
||||
if (item === null) {
|
||||
return undefined;
|
||||
}
|
||||
return JSON.parse(item);
|
||||
},
|
||||
setItemAsJson: (key: Keys, value: any): void => window.localStorage.setItem(key, JSON.stringify(value)),
|
||||
};
|
||||
|
@@ -24,7 +24,6 @@ import MkContextMenu from '@/components/MkContextMenu.vue';
|
||||
import { MenuItem } from '@/types/menu';
|
||||
import copyToClipboard from './scripts/copy-to-clipboard';
|
||||
import { showMovedDialog } from './scripts/show-moved-dialog';
|
||||
import { DriveFile } from 'misskey-js/built/entities';
|
||||
|
||||
export const openingWindowsCount = ref(0);
|
||||
|
||||
@@ -420,7 +419,7 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function selectDriveFile(multiple: boolean): Promise<DriveFile[]> {
|
||||
export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), {
|
||||
type: 'file',
|
||||
|
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { version } from '@/config';
|
||||
import * as os from '@/os';
|
||||
@@ -44,7 +44,7 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
let loaded = $ref(false);
|
||||
let serverIsDead = $ref(false);
|
||||
let meta = $ref<misskey.entities.LiteInstanceMetadata | null>(null);
|
||||
let meta = $ref<Misskey.entities.LiteInstanceMetadata | null>(null);
|
||||
|
||||
os.api('meta', {
|
||||
detail: false,
|
||||
|
@@ -285,6 +285,7 @@ const patrons = [
|
||||
'越貝鯛丸',
|
||||
'Nick / pprmint.',
|
||||
'kino3277',
|
||||
'美少女JKぐーちゃん',
|
||||
];
|
||||
|
||||
let thereIsTreasure = $ref($i && !claimedAchievements.includes('foundTreasure'));
|
||||
|
@@ -200,7 +200,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, defineAsyncComponent, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkChart from '@/components/MkChart.vue';
|
||||
import MkObjectView from '@/components/MkObjectView.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
@@ -232,7 +232,7 @@ const props = withDefaults(defineProps<{
|
||||
|
||||
let tab = $ref(props.initialTab);
|
||||
let chartSrc = $ref('per-user-notes');
|
||||
let user = $ref<null | misskey.entities.UserDetailed>();
|
||||
let user = $ref<null | Misskey.entities.UserDetailed>();
|
||||
let init = $ref<ReturnType<typeof createFetcher>>();
|
||||
let info = $ref();
|
||||
let ips = $ref(null);
|
||||
|
@@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import { AuthSession } from 'misskey-js/built/entities';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@/i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
session: AuthSession;
|
||||
session: Misskey.entities.AuthSession;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -58,7 +58,6 @@ function accept() {
|
||||
emit('accepted');
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@@ -43,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { AuthSession } from 'misskey-js/built/entities';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XForm from './auth.form.vue';
|
||||
import MkSignin from '@/components/MkSignin.vue';
|
||||
import * as os from '@/os';
|
||||
@@ -56,7 +56,7 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
let state = $ref<'waiting' | 'accepted' | 'fetch-session-error' | 'denied' | null>(null);
|
||||
let session = $ref<AuthSession | null>(null);
|
||||
let session = $ref<Misskey.entities.AuthSession | null>(null);
|
||||
|
||||
function accepted() {
|
||||
state = 'accepted';
|
||||
|
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch, provide } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNotes from '@/components/MkNotes.vue';
|
||||
import { $i } from '@/account';
|
||||
import { i18n } from '@/i18n';
|
||||
@@ -41,7 +41,7 @@ const props = defineProps<{
|
||||
clipId: string,
|
||||
}>();
|
||||
|
||||
let clip: misskey.entities.Clip = $ref<misskey.entities.Clip>();
|
||||
let clip: Misskey.entities.Clip = $ref<Misskey.entities.Clip>();
|
||||
let favorited = $ref(false);
|
||||
const pagination = {
|
||||
endpoint: 'clips/notes' as const,
|
||||
|
@@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
@@ -101,7 +101,7 @@ let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false);
|
||||
let localOnly = $ref(props.emoji ? props.emoji.localOnly : false);
|
||||
let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []);
|
||||
let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]);
|
||||
let file = $ref<misskey.entities.DriveFile>();
|
||||
let file = $ref<Misskey.entities.DriveFile>();
|
||||
|
||||
watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => {
|
||||
rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
|
||||
|
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os';
|
||||
import { mainRouter } from '@/router';
|
||||
import { i18n } from '@/i18n';
|
||||
@@ -57,7 +57,7 @@ if (acct.startsWith('https://')) {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
promise = os.api('users/show', Acct.parse(acct));
|
||||
promise = os.api('users/show', Misskey.acct.parse(acct));
|
||||
promise.then(user => {
|
||||
follow(user);
|
||||
});
|
||||
|
@@ -117,7 +117,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkChart from '@/components/MkChart.vue';
|
||||
import MkObjectView from '@/components/MkObjectView.vue';
|
||||
import FormLink from '@/components/form/link.vue';
|
||||
@@ -143,8 +143,8 @@ const props = defineProps<{
|
||||
|
||||
let tab = $ref('overview');
|
||||
let chartSrc = $ref('instance-requests');
|
||||
let meta = $ref<misskey.entities.AdminInstanceMetadata | null>(null);
|
||||
let instance = $ref<misskey.entities.Instance | null>(null);
|
||||
let meta = $ref<Misskey.entities.AdminInstanceMetadata | null>(null);
|
||||
let instance = $ref<Misskey.entities.Instance | null>(null);
|
||||
let suspended = $ref(false);
|
||||
let isBlocked = $ref(false);
|
||||
let faviconUrl = $ref<string | null>(null);
|
||||
|
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkPagination ref="pagingComponent" :pagination="pagination">
|
||||
<template #default="{ items }">
|
||||
<div class="_gaps_s">
|
||||
<MkInviteCode v-for="item in (items as Invite[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
|
||||
<MkInviteCode v-for="item in (items as Misskey.entities.Invite[])" :key="item.id" :invite="item" :onDeleted="deleted"/>
|
||||
</div>
|
||||
</template>
|
||||
</MkPagination>
|
||||
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, shallowRef } from 'vue';
|
||||
import type { Invite } from 'misskey-js/built/entities';
|
||||
import type * as Misskey from 'misskey-js';
|
||||
import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
|
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { watch } from 'vue';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkTextarea from '@/components/MkTextarea.vue';
|
||||
@@ -127,7 +127,7 @@ async function deleteAntenna() {
|
||||
function addUser() {
|
||||
os.selectUser().then(user => {
|
||||
users = users.trim();
|
||||
users += '\n@' + Acct.toString(user as any);
|
||||
users += '\n@' + Misskey.acct.toString(user as any);
|
||||
users = users.trim();
|
||||
});
|
||||
}
|
||||
|
@@ -28,18 +28,17 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { antennasCache } from '@/cache';
|
||||
import { api } from '@/os';
|
||||
import { onActivated } from 'vue';
|
||||
import { infoImageUrl } from '@/instance';
|
||||
|
||||
const antennas = $computed(() => antennasCache.value.value ?? []);
|
||||
|
||||
function fetch() {
|
||||
antennasCache.fetch(() => api('antennas/list'));
|
||||
antennasCache.fetch();
|
||||
}
|
||||
|
||||
fetch();
|
||||
@@ -62,7 +61,7 @@ definePageMetadata({
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
antennasCache.fetch(() => api('antennas/list'));
|
||||
antennasCache.fetch();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@@ -42,7 +42,7 @@ import { $i } from '@/account';
|
||||
const items = $computed(() => userListsCache.value.value ?? []);
|
||||
|
||||
function fetch() {
|
||||
userListsCache.fetch(() => os.api('users/lists/list'));
|
||||
userListsCache.fetch();
|
||||
}
|
||||
|
||||
fetch();
|
||||
|
@@ -48,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os';
|
||||
import { mainRouter } from '@/router';
|
||||
@@ -59,7 +60,6 @@ import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import { userListsCache } from '@/cache';
|
||||
import { UserList, UserLite } from 'misskey-js/built/entities';
|
||||
import { $i } from '@/account';
|
||||
import { defaultStore } from '@/store';
|
||||
const {
|
||||
@@ -72,8 +72,8 @@ const props = defineProps<{
|
||||
|
||||
const FETCH_USERS_LIMIT = 20;
|
||||
|
||||
let list = $ref<UserList | null>(null);
|
||||
let users = $ref<UserLite[]>([]);
|
||||
let list = $ref<Misskey.entities.UserList | null>(null);
|
||||
let users = $ref<Misskey.entities.UserLite[]>([]);
|
||||
let queueUserIds = $ref<string[]>([]);
|
||||
let fetching = $ref(true);
|
||||
const isPublic = ref(false);
|
||||
|
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
|
||||
<div class="_margin">
|
||||
<MkButton v-if="!showNext && hasNext" :class="$style.loadNext" @click="showNext = true"><i class="ti ti-chevron-up"></i></MkButton>
|
||||
<MkButton v-if="!showNext" :class="$style.loadNext" @click="showNext = true"><i class="ti ti-chevron-up"></i></MkButton>
|
||||
<div class="_margin _gaps_s">
|
||||
<MkRemoteCaution v-if="note.user.host != null" :href="note.url ?? note.uri"/>
|
||||
<MkNoteDetailed :key="note.id" v-model:note="note" :class="$style.note"/>
|
||||
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</MkA>
|
||||
</div>
|
||||
</div>
|
||||
<MkButton v-if="!showPrev && hasPrev" :class="$style.loadPrev" @click="showPrev = true"><i class="ti ti-chevron-down"></i></MkButton>
|
||||
<MkButton v-if="!showPrev" :class="$style.loadPrev" @click="showPrev = true"><i class="ti ti-chevron-down"></i></MkButton>
|
||||
</div>
|
||||
|
||||
<div v-if="showPrev" class="_margin">
|
||||
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, watch } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
|
||||
import MkNotes from '@/components/MkNotes.vue';
|
||||
import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
|
||||
@@ -61,10 +61,8 @@ const props = defineProps<{
|
||||
noteId: string;
|
||||
}>();
|
||||
|
||||
let note = $ref<null | misskey.entities.Note>();
|
||||
let note = $ref<null | Misskey.entities.Note>();
|
||||
let clips = $ref();
|
||||
let hasPrev = $ref(false);
|
||||
let hasNext = $ref(false);
|
||||
let showPrev = $ref(false);
|
||||
let showNext = $ref(false);
|
||||
let error = $ref();
|
||||
@@ -89,8 +87,6 @@ const nextPagination = {
|
||||
};
|
||||
|
||||
function fetchNote() {
|
||||
hasPrev = false;
|
||||
hasNext = false;
|
||||
showPrev = false;
|
||||
showNext = false;
|
||||
note = null;
|
||||
@@ -102,20 +98,8 @@ function fetchNote() {
|
||||
os.api('notes/clips', {
|
||||
noteId: note.id,
|
||||
}),
|
||||
os.api('users/notes', {
|
||||
userId: note.userId,
|
||||
untilId: note.id,
|
||||
limit: 1,
|
||||
}),
|
||||
os.api('users/notes', {
|
||||
userId: note.userId,
|
||||
sinceId: note.id,
|
||||
limit: 1,
|
||||
}),
|
||||
]).then(([_clips, prev, next]) => {
|
||||
]).then(([_clips]) => {
|
||||
clips = _clips;
|
||||
hasPrev = prev.length !== 0;
|
||||
hasNext = next.length !== 0;
|
||||
});
|
||||
}).catch(err => {
|
||||
error = err;
|
||||
|
@@ -16,7 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</div>
|
||||
-->
|
||||
<div class="banner">
|
||||
<img v-if="page.eyeCatchingImageId" :src="page.eyeCatchingImage.url"/>
|
||||
<MkMediaImage
|
||||
v-if="page.eyeCatchingImageId"
|
||||
:image="page.eyeCatchingImage"
|
||||
:cover="true"
|
||||
:disableImageLink="true"
|
||||
class="thumbnail"
|
||||
/>
|
||||
</div>
|
||||
<div class="content">
|
||||
<XPage :page="page"/>
|
||||
@@ -56,8 +62,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkContainer :max-height="300" :foldable="true" class="other">
|
||||
<template #icon><i class="ti ti-clock"></i></template>
|
||||
<template #header>{{ i18n.ts.recentPosts }}</template>
|
||||
<MkPagination v-slot="{items}" :pagination="otherPostsPagination">
|
||||
<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/>
|
||||
<MkPagination v-slot="{items}" :pagination="otherPostsPagination" :class="$style.relatedPagesRoot" class="_gaps">
|
||||
<MkPagePreview v-for="page in items" :key="page.id" :page="page" :class="$style.relatedPagesItem"/>
|
||||
</MkPagination>
|
||||
</MkContainer>
|
||||
</div>
|
||||
@@ -74,6 +80,7 @@ import XPage from '@/components/page/page.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import * as os from '@/os';
|
||||
import { url } from '@/config';
|
||||
import MkMediaImage from '@/components/MkMediaImage.vue';
|
||||
import MkFollowButton from '@/components/MkFollowButton.vue';
|
||||
import MkContainer from '@/components/MkContainer.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
@@ -204,11 +211,14 @@ definePageMetadata(computed(() => page ? {
|
||||
}
|
||||
|
||||
> .banner {
|
||||
> img {
|
||||
> .thumbnail {
|
||||
// TODO: 良い感じのアスペクト比で表示
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
height: auto;
|
||||
aspect-ratio: 3/1;
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
@@ -279,3 +289,13 @@ definePageMetadata(computed(() => page ? {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style module>
|
||||
.relatedPagesRoot {
|
||||
padding: var(--margin);
|
||||
}
|
||||
|
||||
.relatedPagesItem > article {
|
||||
background-color: var(--panelHighlight) !important;
|
||||
}
|
||||
</style>
|
||||
|
@@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<MkSpacer :contentMax="800">
|
||||
<div :class="$style.root">
|
||||
<div :class="$style.editor" class="_panel">
|
||||
<PrismEditor v-model="code" class="_code code" :highlight="highlighter" :lineNumbers="false"/>
|
||||
<PrismEditor v-model="code" class="_monospace" :class="$style.code" :highlight="highlighter" :lineNumbers="false"/>
|
||||
<MkButton style="position: absolute; top: 8px; right: 8px;" primary @click="run()"><i class="ti ti-player-play"></i></MkButton>
|
||||
</div>
|
||||
|
||||
@@ -175,6 +175,14 @@ definePageMetadata({
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.code {
|
||||
background: #2d2d2d;
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.ui {
|
||||
padding: 32px;
|
||||
}
|
||||
|
@@ -4,45 +4,110 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<template>
|
||||
<MkModal
|
||||
ref="dialogEl"
|
||||
:preferType="'dialog'"
|
||||
:zPriority="'low'"
|
||||
@click="cancel"
|
||||
<MkModalWindow
|
||||
ref="dialog"
|
||||
:width="500"
|
||||
:height="550"
|
||||
@close="cancel"
|
||||
@closed="emit('closed')"
|
||||
>
|
||||
<div :class="$style.root" class="_gaps_m">
|
||||
<I18n :src="i18n.ts._2fa.step1" tag="div">
|
||||
<template #a>
|
||||
<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
|
||||
<template #header>{{ i18n.ts.setupOf2fa }}</template>
|
||||
|
||||
<div style="overflow-x: clip;">
|
||||
<Transition
|
||||
mode="out-in"
|
||||
:enterActiveClass="$style.transition_x_enterActive"
|
||||
:leaveActiveClass="$style.transition_x_leaveActive"
|
||||
:enterFromClass="$style.transition_x_enterFrom"
|
||||
:leaveToClass="$style.transition_x_leaveTo"
|
||||
>
|
||||
<template v-if="page === 0">
|
||||
<div style="height: 100cqh; overflow: auto; text-align: center;">
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
<div class="_gaps">
|
||||
<I18n :src="i18n.ts._2fa.step1" tag="div">
|
||||
<template #a>
|
||||
<a href="https://authy.com/" rel="noopener" target="_blank" class="_link">Authy</a>
|
||||
</template>
|
||||
<template #b>
|
||||
<a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a>
|
||||
</template>
|
||||
</I18n>
|
||||
<div>{{ i18n.ts._2fa.step2 }}<br>{{ i18n.ts._2fa.step2Click }}</div>
|
||||
<a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a>
|
||||
<MkKeyValue :copy="twoFactorData.url">
|
||||
<template #key>{{ i18n.ts._2fa.step2Uri }}</template>
|
||||
<template #value>{{ twoFactorData.url }}</template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
<div class="_buttonsCenter" style="margin-top: 16px;">
|
||||
<MkButton rounded @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
<MkButton primary rounded gradate @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</template>
|
||||
<template #b>
|
||||
<a href="https://support.google.com/accounts/answer/1066447" rel="noopener" target="_blank" class="_link">Google Authenticator</a>
|
||||
<template v-else-if="page === 1">
|
||||
<div style="height: 100cqh; overflow: auto;">
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
<div class="_gaps">
|
||||
<div>{{ i18n.ts._2fa.step3Title }}</div>
|
||||
<MkInput v-model="token" autocomplete="one-time-code"></MkInput>
|
||||
<div>{{ i18n.ts._2fa.step3 }}</div>
|
||||
</div>
|
||||
<div class="_buttonsCenter" style="margin-top: 16px;">
|
||||
<MkButton rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton>
|
||||
<MkButton primary rounded gradate @click="tokenDone">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</template>
|
||||
</I18n>
|
||||
<div>
|
||||
{{ i18n.ts._2fa.step2 }}<br>
|
||||
{{ i18n.ts._2fa.step2Click }}
|
||||
</div>
|
||||
<a :href="twoFactorData.url"><img :class="$style.qr" :src="twoFactorData.qr"></a>
|
||||
<MkKeyValue :copy="twoFactorData.url">
|
||||
<template #key>{{ i18n.ts._2fa.step2Url }}</template>
|
||||
<template #value>{{ twoFactorData.url }}</template>
|
||||
</MkKeyValue>
|
||||
<div class="_buttons">
|
||||
<MkButton primary @click="ok">{{ i18n.ts.next }}</MkButton>
|
||||
<MkButton @click="cancel">{{ i18n.ts.cancel }}</MkButton>
|
||||
</div>
|
||||
<template v-else-if="page === 2">
|
||||
<div style="height: 100cqh; overflow: auto;">
|
||||
<MkSpacer :marginMin="20" :marginMax="28">
|
||||
<div class="_gaps">
|
||||
<div style="text-align: center;">{{ i18n.ts._2fa.setupCompleted }}🎉</div>
|
||||
<div style="text-align: center;">{{ i18n.ts._2fa.step4 }}</div>
|
||||
<div style="text-align: center; font-weight: bold;">{{ i18n.ts._2fa.checkBackupCodesBeforeCloseThisWizard }}</div>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-key"></i></template>
|
||||
<template #label>{{ i18n.ts._2fa.backupCodes }}</template>
|
||||
|
||||
<div class="_gaps">
|
||||
<MkInfo warn>{{ i18n.ts._2fa.backupCodesDescription }}</MkInfo>
|
||||
|
||||
<div v-for="(code, i) in backupCodes" :key="code" class="_gaps_s">
|
||||
<MkKeyValue :copy="code">
|
||||
<template #key>#{{ i + 1 }}</template>
|
||||
<template #value><code class="_monospace">{{ code }}</code></template>
|
||||
</MkKeyValue>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
</div>
|
||||
<div class="_buttonsCenter" style="margin-top: 16px;">
|
||||
<MkButton primary rounded gradate @click="allDone">{{ i18n.ts.done }}</MkButton>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
</div>
|
||||
</template>
|
||||
</Transition>
|
||||
</div>
|
||||
</MkModal>
|
||||
</MkModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef, ref } from 'vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkModal from '@/components/MkModal.vue';
|
||||
import MkModalWindow from '@/components/MkModalWindow.vue';
|
||||
import MkKeyValue from '@/components/MkKeyValue.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import { confetti } from '@/scripts/confetti';
|
||||
|
||||
defineProps<{
|
||||
twoFactorData: {
|
||||
@@ -52,36 +117,53 @@ defineProps<{
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(ev: 'ok'): void;
|
||||
(ev: 'cancel'): void;
|
||||
(ev: 'closed'): void;
|
||||
}>();
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
emit('closed');
|
||||
};
|
||||
const dialog = shallowRef<InstanceType<typeof MkModalWindow>>();
|
||||
const page = ref(0);
|
||||
const token = ref<string | number | null>(null);
|
||||
const backupCodes = ref<string[]>();
|
||||
|
||||
const ok = () => {
|
||||
emit('ok');
|
||||
emit('closed');
|
||||
};
|
||||
function cancel() {
|
||||
dialog.value.close();
|
||||
}
|
||||
|
||||
async function tokenDone() {
|
||||
const res = await os.apiWithDialog('i/2fa/done', {
|
||||
token: token.value.toString(),
|
||||
});
|
||||
|
||||
backupCodes.value = res.backupCodes;
|
||||
|
||||
page.value++;
|
||||
|
||||
confetti({
|
||||
duration: 1000 * 3,
|
||||
});
|
||||
}
|
||||
|
||||
function allDone() {
|
||||
dialog.value.close();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
position: relative;
|
||||
margin: auto;
|
||||
padding: 32px;
|
||||
min-width: 320px;
|
||||
max-width: calc(100svw - 64px);
|
||||
box-sizing: border-box;
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
.transition_x_enterActive,
|
||||
.transition_x_leaveActive {
|
||||
transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1);
|
||||
}
|
||||
.transition_x_enterFrom {
|
||||
opacity: 0;
|
||||
transform: translateX(50px);
|
||||
}
|
||||
.transition_x_leaveTo {
|
||||
opacity: 0;
|
||||
transform: translateX(-50px);
|
||||
}
|
||||
|
||||
.qr {
|
||||
width: 20em;
|
||||
max-width: 100%;
|
||||
width: 200px;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
@@ -8,20 +8,29 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>{{ i18n.ts['2fa'] }}</template>
|
||||
|
||||
<div v-if="$i" class="_gaps_s">
|
||||
<MkFolder>
|
||||
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'partial'" warn>
|
||||
{{ i18n.ts._2fa.backupCodeUsedWarning }}
|
||||
</MkInfo>
|
||||
<MkInfo v-if="$i.twoFactorEnabled && $i.twoFactorBackupCodesStock === 'none'" warn>
|
||||
{{ i18n.ts._2fa.backupCodesExhaustedWarning }}
|
||||
</MkInfo>
|
||||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #icon><i class="ti ti-shield-lock"></i></template>
|
||||
<template #label>{{ i18n.ts.totp }}</template>
|
||||
<template #caption>{{ i18n.ts.totpDescription }}</template>
|
||||
<template #suffix><i v-if="$i.twoFactorEnabled" class="ti ti-check" style="color: var(--success)"></i></template>
|
||||
|
||||
<div v-if="$i.twoFactorEnabled" class="_gaps_s">
|
||||
<div v-text="i18n.ts._2fa.alreadyRegistered"/>
|
||||
<template v-if="$i.securityKeysList.length > 0">
|
||||
<MkButton @click="renewTOTP">{{ i18n.ts._2fa.renewTOTP }}</MkButton>
|
||||
<MkInfo>{{ i18n.ts._2fa.whyTOTPOnlyRenew }}</MkInfo>
|
||||
</template>
|
||||
<MkButton v-else @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
||||
<MkButton v-else danger @click="unregisterTOTP">{{ i18n.ts.unregister }}</MkButton>
|
||||
</div>
|
||||
|
||||
<MkButton v-else-if="!twoFactorData && !$i.twoFactorEnabled" @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
||||
<MkButton v-else-if="!$i.twoFactorEnabled" primary gradate @click="registerTOTP">{{ i18n.ts._2fa.registerTOTP }}</MkButton>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder>
|
||||
@@ -29,16 +38,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<template #label>{{ i18n.ts.securityKeyAndPasskey }}</template>
|
||||
<div class="_gaps_s">
|
||||
<MkInfo>
|
||||
{{ i18n.ts._2fa.securityKeyInfo }}<br>
|
||||
<br>
|
||||
{{ i18n.ts._2fa.chromePasskeyNotSupported }}
|
||||
{{ i18n.ts._2fa.securityKeyInfo }}
|
||||
</MkInfo>
|
||||
|
||||
<MkInfo v-if="!supportsCredentials" warn>
|
||||
<MkInfo v-if="!webAuthnSupported()" warn>
|
||||
{{ i18n.ts._2fa.securityKeyNotSupported }}
|
||||
</MkInfo>
|
||||
|
||||
<MkInfo v-else-if="supportsCredentials && !$i.twoFactorEnabled" warn>
|
||||
<MkInfo v-else-if="webAuthnSupported() && !$i.twoFactorEnabled" warn>
|
||||
{{ i18n.ts._2fa.registerTOTPBeforeKey }}
|
||||
</MkInfo>
|
||||
|
||||
@@ -66,8 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, defineAsyncComponent } from 'vue';
|
||||
import { hostname } from '@/config';
|
||||
import { byteify, hexify, stringify } from '@/scripts/2fa';
|
||||
import { supported as webAuthnSupported, create as webAuthnCreate, parseCreationOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkInfo from '@/components/MkInfo.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
@@ -85,11 +91,9 @@ withDefaults(defineProps<{
|
||||
first: false,
|
||||
});
|
||||
|
||||
const twoFactorData = ref<any>(null);
|
||||
const supportsCredentials = ref(!!navigator.credentials);
|
||||
const usePasswordLessLogin = $computed(() => $i!.usePasswordLessLogin);
|
||||
const usePasswordLessLogin = $computed(() => $i?.usePasswordLessLogin ?? false);
|
||||
|
||||
async function registerTOTP() {
|
||||
async function registerTOTP(): Promise<void> {
|
||||
const password = await os.inputText({
|
||||
title: i18n.ts._2fa.registerTOTP,
|
||||
text: i18n.ts._2fa.passwordToTOTP,
|
||||
@@ -102,34 +106,12 @@ async function registerTOTP() {
|
||||
password: password.result,
|
||||
});
|
||||
|
||||
const qrdialog = await new Promise<boolean>(res => {
|
||||
os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
|
||||
twoFactorData,
|
||||
}, {
|
||||
'ok': () => res(true),
|
||||
'cancel': () => res(false),
|
||||
}, 'closed');
|
||||
});
|
||||
if (!qrdialog) return;
|
||||
|
||||
const token = await os.inputNumber({
|
||||
title: i18n.ts._2fa.step3Title,
|
||||
text: i18n.ts._2fa.step3,
|
||||
autocomplete: 'one-time-code',
|
||||
});
|
||||
if (token.canceled) return;
|
||||
|
||||
await os.apiWithDialog('i/2fa/done', {
|
||||
token: token.result.toString(),
|
||||
});
|
||||
|
||||
await os.alert({
|
||||
type: 'success',
|
||||
text: i18n.ts._2fa.step4,
|
||||
});
|
||||
os.popup(defineAsyncComponent(() => import('./2fa.qrdialog.vue')), {
|
||||
twoFactorData,
|
||||
}, {}, 'closed');
|
||||
}
|
||||
|
||||
function unregisterTOTP() {
|
||||
function unregisterTOTP(): void {
|
||||
os.inputText({
|
||||
title: i18n.ts.password,
|
||||
type: 'password',
|
||||
@@ -147,7 +129,7 @@ function unregisterTOTP() {
|
||||
});
|
||||
}
|
||||
|
||||
function renewTOTP() {
|
||||
function renewTOTP(): void {
|
||||
os.confirm({
|
||||
type: 'question',
|
||||
title: i18n.ts._2fa.renewTOTP,
|
||||
@@ -206,8 +188,10 @@ async function addSecurityKey() {
|
||||
});
|
||||
if (password.canceled) return;
|
||||
|
||||
const challenge: any = await os.apiWithDialog('i/2fa/register-key', {
|
||||
password: password.result,
|
||||
const registrationOptions = parseCreationOptionsFromJSON({
|
||||
publicKey: await os.apiWithDialog('i/2fa/register-key', {
|
||||
password: password.result,
|
||||
}),
|
||||
});
|
||||
|
||||
const name = await os.inputText({
|
||||
@@ -219,26 +203,8 @@ async function addSecurityKey() {
|
||||
});
|
||||
if (name.canceled) return;
|
||||
|
||||
const webAuthnCreation = navigator.credentials.create({
|
||||
publicKey: {
|
||||
challenge: byteify(challenge.challenge, 'base64'),
|
||||
rp: {
|
||||
id: hostname,
|
||||
name: 'Misskey',
|
||||
},
|
||||
user: {
|
||||
id: byteify($i!.id, 'ascii'),
|
||||
name: $i!.username,
|
||||
displayName: $i!.name,
|
||||
},
|
||||
pubKeyCredParams: [{ alg: -7, type: 'public-key' }],
|
||||
timeout: 60000,
|
||||
attestation: 'direct',
|
||||
},
|
||||
}) as Promise<PublicKeyCredential & { response: AuthenticatorAttestationResponse; } | null>;
|
||||
|
||||
const credential = await os.promiseDialog(
|
||||
webAuthnCreation,
|
||||
webAuthnCreate(registrationOptions),
|
||||
null,
|
||||
() => {}, // ユーザーのキャンセルはrejectなのでエラーダイアログを出さない
|
||||
i18n.ts._2fa.tapSecurityKey,
|
||||
@@ -248,10 +214,7 @@ async function addSecurityKey() {
|
||||
await os.apiWithDialog('i/2fa/key-done', {
|
||||
password: password.result,
|
||||
name: name.result,
|
||||
challengeId: challenge.challengeId,
|
||||
// we convert each 16 bits to a string to serialise
|
||||
clientDataJSON: stringify(credential.response.clientDataJSON),
|
||||
attestationObject: hexify(credential.response.attestationObject),
|
||||
credential: credential.toJSON(),
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -48,13 +48,18 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<div class="_gaps_s">
|
||||
<MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
|
||||
<MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
|
||||
<MkSwitch v-model="largeNoteReactions">{{ i18n.ts.largeNoteReactions }}</MkSwitch>
|
||||
<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
|
||||
<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
|
||||
<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
|
||||
<MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch>
|
||||
<MkSwitch v-model="loadRawImages">{{ i18n.ts.loadRawImages }}</MkSwitch>
|
||||
<MkSwitch v-model="useReactionPickerForContextMenu">{{ i18n.ts.useReactionPickerForContextMenu }}</MkSwitch>
|
||||
<MkRadios v-model="reactionsDisplaySize">
|
||||
<template #label>{{ i18n.ts.reactionsDisplaySize }}</template>
|
||||
<option value="small">{{ i18n.ts.small }}</option>
|
||||
<option value="medium">{{ i18n.ts.medium }}</option>
|
||||
<option value="large">{{ i18n.ts.large }}</option>
|
||||
</MkRadios>
|
||||
</div>
|
||||
|
||||
<MkSelect v-model="instanceTicker">
|
||||
@@ -98,6 +103,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<option value="vertical"><i class="ti ti-carousel-vertical"></i> {{ i18n.ts.vertical }}</option>
|
||||
<option value="horizontal"><i class="ti ti-carousel-horizontal"></i> {{ i18n.ts.horizontal }}</option>
|
||||
</MkRadios>
|
||||
|
||||
<MkButton @click="testNotification('client')">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
@@ -194,6 +201,7 @@ import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { miLocalStorage } from '@/local-storage';
|
||||
import { isWebKit } from '@/scripts/useragent';
|
||||
import { testNotification } from '@/scripts/test-notification';
|
||||
|
||||
const lang = ref(miLocalStorage.getItem('lang'));
|
||||
const fontSize = ref(miLocalStorage.getItem('fontSize'));
|
||||
@@ -213,7 +221,7 @@ const overridedDeviceKind = computed(defaultStore.makeGetterSetter('overridedDev
|
||||
const serverDisconnectedBehavior = computed(defaultStore.makeGetterSetter('serverDisconnectedBehavior'));
|
||||
const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNoteActionsOnlyHover'));
|
||||
const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter'));
|
||||
const largeNoteReactions = computed(defaultStore.makeGetterSetter('largeNoteReactions'));
|
||||
const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize'));
|
||||
const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes'));
|
||||
const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v));
|
||||
const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal'));
|
||||
@@ -274,6 +282,7 @@ watch([
|
||||
instanceTicker,
|
||||
overridedDeviceKind,
|
||||
mediaListWithOneImageAppearance,
|
||||
reactionsDisplaySize,
|
||||
], async () => {
|
||||
await reloadAsk();
|
||||
});
|
||||
|
@@ -59,8 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { toString } from 'misskey-js/built/acct';
|
||||
import { UserDetailed } from 'misskey-js/built/entities';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import FormInfo from '@/components/MkInfo.vue';
|
||||
import MkInput from '@/components/MkInput.vue';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
@@ -73,7 +72,7 @@ import { $i } from '@/account';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
|
||||
const moveToAccount = ref('');
|
||||
const movedTo = ref<UserDetailed>();
|
||||
const movedTo = ref<Misskey.entities.UserDetailed>();
|
||||
const accountAliases = ref(['']);
|
||||
|
||||
async function init() {
|
||||
@@ -85,7 +84,7 @@ async function init() {
|
||||
|
||||
if ($i?.alsoKnownAs && $i.alsoKnownAs.length > 0) {
|
||||
const alsoKnownAs = await os.api('users/show', { userIds: $i.alsoKnownAs });
|
||||
accountAliases.value = (alsoKnownAs && alsoKnownAs.length > 0) ? alsoKnownAs.map(user => `@${toString(user)}`) : [''];
|
||||
accountAliases.value = (alsoKnownAs && alsoKnownAs.length > 0) ? alsoKnownAs.map(user => `@${Misskey.acct.toString(user)}`) : [''];
|
||||
} else {
|
||||
accountAliases.value = [''];
|
||||
}
|
||||
|
@@ -12,6 +12,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<FormLink @click="readAllUnreadNotes">{{ i18n.ts.markAsReadAllUnreadNotes }}</FormLink>
|
||||
</div>
|
||||
</FormSection>
|
||||
<FormSection>
|
||||
<div class="_gaps_m">
|
||||
<FormLink @click="testNotification('server')">{{ i18n.ts._notification.sendTestNotification }}</FormLink>
|
||||
</div>
|
||||
</FormSection>
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.pushNotification }}</template>
|
||||
|
||||
@@ -41,6 +46,7 @@ import { i18n } from '@/i18n';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
|
||||
import { notificationTypes } from '@/const';
|
||||
import { testNotification } from '@/scripts/test-notification';
|
||||
|
||||
let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
|
||||
let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer);
|
||||
|
@@ -33,8 +33,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
// SPECIFICATION: https://misskey-hub.net/docs/features/share-form.html
|
||||
|
||||
import { } from 'vue';
|
||||
import { noteVisibilities } from 'misskey-js';
|
||||
import * as Acct from 'misskey-js/built/acct';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkPostForm from '@/components/MkPostForm.vue';
|
||||
@@ -45,7 +43,7 @@ import { i18n } from '@/i18n';
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const localOnlyQuery = urlParams.get('localOnly');
|
||||
const visibilityQuery = urlParams.get('visibility') as typeof noteVisibilities[number];
|
||||
const visibilityQuery = urlParams.get('visibility') as typeof Misskey.noteVisibilities[number];
|
||||
|
||||
let state = $ref('fetching' as 'fetching' | 'writing' | 'posted');
|
||||
let title = $ref(urlParams.get('title'));
|
||||
@@ -54,7 +52,7 @@ const url = urlParams.get('url');
|
||||
let initialText = $ref<string | undefined>();
|
||||
let reply = $ref<Misskey.entities.Note | undefined>();
|
||||
let renote = $ref<Misskey.entities.Note | undefined>();
|
||||
let visibility = $ref(noteVisibilities.includes(visibilityQuery) ? visibilityQuery : undefined);
|
||||
let visibility = $ref(Misskey.noteVisibilities.includes(visibilityQuery) ? visibilityQuery : undefined);
|
||||
let localOnly = $ref(localOnlyQuery === '0' ? false : localOnlyQuery === '1' ? true : undefined);
|
||||
let files = $ref([] as Misskey.entities.DriveFile[]);
|
||||
let visibleUsers = $ref([] as Misskey.entities.User[]);
|
||||
@@ -74,7 +72,7 @@ async function init() {
|
||||
await Promise.all(
|
||||
[
|
||||
...(visibleUserIds ? visibleUserIds.split(',').map(userId => ({ userId })) : []),
|
||||
...(visibleAccts ? visibleAccts.split(',').map(Acct.parse) : []),
|
||||
...(visibleAccts ? visibleAccts.split(',').map(Misskey.acct.parse) : []),
|
||||
]
|
||||
// TypeScriptの指示通りに変換する
|
||||
.map(q => 'username' in q ? { username: q.username, host: q.host === null ? undefined : q.host } : q)
|
||||
|
@@ -34,6 +34,8 @@ import { i18n } from '@/i18n';
|
||||
import { instance } from '@/instance';
|
||||
import { $i } from '@/account';
|
||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||
import { miLocalStorage } from '@/local-storage';
|
||||
import { antennasCache, userListsCache } from '@/cache';
|
||||
|
||||
provide('shouldOmitHeaderTitle', true);
|
||||
|
||||
@@ -56,7 +58,7 @@ function top(): void {
|
||||
}
|
||||
|
||||
async function chooseList(ev: MouseEvent): Promise<void> {
|
||||
const lists = await os.api('users/lists/list');
|
||||
const lists = await userListsCache.fetch();
|
||||
const items = lists.map(list => ({
|
||||
type: 'link' as const,
|
||||
text: list.name,
|
||||
@@ -66,7 +68,7 @@ async function chooseList(ev: MouseEvent): Promise<void> {
|
||||
}
|
||||
|
||||
async function chooseAntenna(ev: MouseEvent): Promise<void> {
|
||||
const antennas = await os.api('antennas/list');
|
||||
const antennas = await antennasCache.fetch();
|
||||
const items = antennas.map(antenna => ({
|
||||
type: 'link' as const,
|
||||
text: antenna.name,
|
||||
|
@@ -11,13 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkAchievements from '@/components/MkAchievements.vue';
|
||||
import { claimAchievement } from '@/scripts/achievements';
|
||||
import { $i } from '@/account';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
let timer: number | null;
|
||||
|
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { Chart, ChartDataset } from 'chart.js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import gradient from 'chartjs-plugin-gradient';
|
||||
import * as os from '@/os';
|
||||
import { defaultStore } from '@/store';
|
||||
@@ -29,7 +29,7 @@ import MkChartLegend from '@/components/MkChartLegend.vue';
|
||||
initChart();
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
||||
|
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, nextTick, watch } from 'vue';
|
||||
import { Chart } from 'chart.js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import * as os from '@/os';
|
||||
import { defaultStore } from '@/store';
|
||||
import { useChartTooltip } from '@/scripts/use-chart-tooltip';
|
||||
@@ -26,7 +26,7 @@ initChart();
|
||||
|
||||
const props = defineProps<{
|
||||
src: string;
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const rootEl = $shallowRef<HTMLDivElement>(null);
|
||||
|
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { Chart, ChartDataset } from 'chart.js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import gradient from 'chartjs-plugin-gradient';
|
||||
import * as os from '@/os';
|
||||
import { defaultStore } from '@/store';
|
||||
@@ -29,7 +29,7 @@ import MkChartLegend from '@/components/MkChartLegend.vue';
|
||||
initChart();
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
||||
|
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { Chart, ChartDataset } from 'chart.js';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import gradient from 'chartjs-plugin-gradient';
|
||||
import * as os from '@/os';
|
||||
import { defaultStore } from '@/store';
|
||||
@@ -29,7 +29,7 @@ import MkChartLegend from '@/components/MkChartLegend.vue';
|
||||
initChart();
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const chartEl = $shallowRef<HTMLCanvasElement>(null);
|
||||
|
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import XHeatmap from './activity.heatmap.vue';
|
||||
import XPv from './activity.pv.vue';
|
||||
import XNotes from './activity.notes.vue';
|
||||
@@ -35,7 +35,7 @@ import XFollowing from './activity.following.vue';
|
||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
</script>
|
||||
|
@@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const pagination = {
|
||||
|
@@ -13,12 +13,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkFlashPreview from '@/components/MkFlashPreview.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const pagination = {
|
||||
|
@@ -15,12 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import * as misskey from 'misskey-js';
|
||||
import * as Misskey from 'misskey-js';
|
||||
import MkUserInfo from '@/components/MkUserInfo.vue';
|
||||
import MkPagination from '@/components/MkPagination.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
user: Misskey.entities.User;
|
||||
type: 'following' | 'followers';
|
||||
}>();
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user