Compare commits
21 Commits
feat/bundl
...
renovate/b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31e87343c6 | ||
|
|
0471e457fe | ||
|
|
260d35e2f0 | ||
|
|
3ff9d9f4fd | ||
|
|
27991a3bc8 | ||
|
|
b5f86e5210 | ||
|
|
16cde5568d | ||
|
|
bf07796b6b | ||
|
|
08b131ec33 | ||
|
|
1312fe34c1 | ||
|
|
97563910fa | ||
|
|
96a7c4a568 | ||
|
|
fee6f9fcc2 | ||
|
|
50724b6ab8 | ||
|
|
e61263cff0 | ||
|
|
d073fe6b02 | ||
|
|
ce858a676b | ||
|
|
733a391d86 | ||
|
|
0e25a0fb81 | ||
|
|
1fd87bd2e4 | ||
|
|
ebc54b1f82 |
17
CHANGELOG.md
17
CHANGELOG.md
@@ -6,9 +6,24 @@
|
|||||||
|
|
||||||
### Client
|
### Client
|
||||||
- Feat: 設定の管理が強化されました
|
- Feat: 設定の管理が強化されました
|
||||||
- 自動でバックアップされるように
|
- 内部処理が一新され、安定性とパフォーマンスが向上しました
|
||||||
|
- 全てのクライアント設定がエクスポート(バックアップ)/インポート対象に含まれるようになりました
|
||||||
|
- プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました
|
||||||
|
- 自動で設定データをサーバーにバックアップできるように
|
||||||
|
- 設定→設定のプロファイル→自動バックアップ で有効にできます
|
||||||
|
- 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能)
|
||||||
- 任意の設定項目をデバイス間で同期できるように
|
- 任意の設定項目をデバイス間で同期できるように
|
||||||
|
- 設定項目の「...」メニュー→「デバイス間で同期」
|
||||||
|
- 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます
|
||||||
|
- 任意の設定項目を初期値にリセットできるように
|
||||||
|
- 設定項目の「...」メニュー→「初期値にリセット」
|
||||||
|
- アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように
|
||||||
|
- 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます
|
||||||
|
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
|
||||||
|
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
|
||||||
|
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
|
||||||
- Feat: 画面を重ねて表示するオプションを実装(実験的)
|
- Feat: 画面を重ねて表示するオプションを実装(実験的)
|
||||||
|
- 設定 → その他 → 実験的機能 → Enable stacking router view
|
||||||
- Enhance: プラグインの管理が強化されました
|
- Enhance: プラグインの管理が強化されました
|
||||||
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
||||||
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"version": "2025.3.2-beta.6",
|
"version": "2025.3.2-beta.9",
|
||||||
"codename": "nasubi",
|
"codename": "nasubi",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
@@ -37,17 +37,17 @@
|
|||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@swc/core-android-arm64": "1.3.11",
|
"@swc/core-android-arm64": "1.3.11",
|
||||||
"@swc/core-darwin-arm64": "1.10.16",
|
"@swc/core-darwin-arm64": "1.11.12",
|
||||||
"@swc/core-darwin-x64": "1.10.16",
|
"@swc/core-darwin-x64": "1.11.12",
|
||||||
"@swc/core-freebsd-x64": "1.3.11",
|
"@swc/core-freebsd-x64": "1.3.11",
|
||||||
"@swc/core-linux-arm-gnueabihf": "1.10.16",
|
"@swc/core-linux-arm-gnueabihf": "1.11.12",
|
||||||
"@swc/core-linux-arm64-gnu": "1.10.16",
|
"@swc/core-linux-arm64-gnu": "1.11.12",
|
||||||
"@swc/core-linux-arm64-musl": "1.10.16",
|
"@swc/core-linux-arm64-musl": "1.11.12",
|
||||||
"@swc/core-linux-x64-gnu": "1.10.16",
|
"@swc/core-linux-x64-gnu": "1.11.12",
|
||||||
"@swc/core-linux-x64-musl": "1.10.16",
|
"@swc/core-linux-x64-musl": "1.11.12",
|
||||||
"@swc/core-win32-arm64-msvc": "1.10.16",
|
"@swc/core-win32-arm64-msvc": "1.11.12",
|
||||||
"@swc/core-win32-ia32-msvc": "1.10.16",
|
"@swc/core-win32-ia32-msvc": "1.11.12",
|
||||||
"@swc/core-win32-x64-msvc": "1.10.16",
|
"@swc/core-win32-x64-msvc": "1.11.12",
|
||||||
"@tensorflow/tfjs": "4.22.0",
|
"@tensorflow/tfjs": "4.22.0",
|
||||||
"@tensorflow/tfjs-node": "4.22.0",
|
"@tensorflow/tfjs-node": "4.22.0",
|
||||||
"bufferutil": "4.0.9",
|
"bufferutil": "4.0.9",
|
||||||
@@ -67,23 +67,23 @@
|
|||||||
"utf-8-validate": "6.0.5"
|
"utf-8-validate": "6.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "3.749.0",
|
"@aws-sdk/client-s3": "3.772.0",
|
||||||
"@aws-sdk/lib-storage": "3.749.0",
|
"@aws-sdk/lib-storage": "3.772.0",
|
||||||
"@discordapp/twemoji": "15.1.0",
|
"@discordapp/twemoji": "15.1.0",
|
||||||
"@fastify/accepts": "5.0.2",
|
"@fastify/accepts": "5.0.2",
|
||||||
"@fastify/cookie": "11.0.2",
|
"@fastify/cookie": "11.0.2",
|
||||||
"@fastify/cors": "10.0.2",
|
"@fastify/cors": "10.1.0",
|
||||||
"@fastify/express": "4.0.2",
|
"@fastify/express": "4.0.2",
|
||||||
"@fastify/http-proxy": "10.0.2",
|
"@fastify/http-proxy": "10.0.2",
|
||||||
"@fastify/multipart": "9.0.3",
|
"@fastify/multipart": "9.0.3",
|
||||||
"@fastify/static": "8.1.0",
|
"@fastify/static": "8.1.1",
|
||||||
"@fastify/view": "10.0.2",
|
"@fastify/view": "10.0.2",
|
||||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||||
"@misskey-dev/summaly": "5.2.0",
|
"@misskey-dev/summaly": "5.2.0",
|
||||||
"@napi-rs/canvas": "0.1.67",
|
"@napi-rs/canvas": "0.1.68",
|
||||||
"@nestjs/common": "11.0.9",
|
"@nestjs/common": "11.0.12",
|
||||||
"@nestjs/core": "11.0.9",
|
"@nestjs/core": "11.0.12",
|
||||||
"@nestjs/testing": "11.0.9",
|
"@nestjs/testing": "11.0.12",
|
||||||
"@peertube/http-signature": "1.7.0",
|
"@peertube/http-signature": "1.7.0",
|
||||||
"@sentry/node": "8.55.0",
|
"@sentry/node": "8.55.0",
|
||||||
"@sentry/profiling-node": "8.55.0",
|
"@sentry/profiling-node": "8.55.0",
|
||||||
@@ -91,7 +91,7 @@
|
|||||||
"@sinonjs/fake-timers": "11.3.1",
|
"@sinonjs/fake-timers": "11.3.1",
|
||||||
"@smithy/node-http-handler": "2.5.0",
|
"@smithy/node-http-handler": "2.5.0",
|
||||||
"@swc/cli": "0.6.0",
|
"@swc/cli": "0.6.0",
|
||||||
"@swc/core": "1.10.16",
|
"@swc/core": "1.11.12",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"accepts": "1.3.8",
|
"accepts": "1.3.8",
|
||||||
"ajv": "8.17.1",
|
"ajv": "8.17.1",
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"bcryptjs": "2.4.3",
|
"bcryptjs": "2.4.3",
|
||||||
"blurhash": "2.0.5",
|
"blurhash": "2.0.5",
|
||||||
"body-parser": "1.20.3",
|
"body-parser": "1.20.3",
|
||||||
"bullmq": "5.41.1",
|
"bullmq": "5.44.3",
|
||||||
"cacheable-lookup": "7.0.0",
|
"cacheable-lookup": "7.0.0",
|
||||||
"cbor": "9.0.2",
|
"cbor": "9.0.2",
|
||||||
"chalk": "5.4.1",
|
"chalk": "5.4.1",
|
||||||
@@ -122,7 +122,7 @@
|
|||||||
"hpagent": "1.2.0",
|
"hpagent": "1.2.0",
|
||||||
"htmlescape": "1.1.1",
|
"htmlescape": "1.1.1",
|
||||||
"http-link-header": "1.1.3",
|
"http-link-header": "1.1.3",
|
||||||
"ioredis": "5.5.0",
|
"ioredis": "5.6.0",
|
||||||
"ip-cidr": "4.0.2",
|
"ip-cidr": "4.0.2",
|
||||||
"ipaddr.js": "2.2.0",
|
"ipaddr.js": "2.2.0",
|
||||||
"is-svg": "5.1.0",
|
"is-svg": "5.1.0",
|
||||||
@@ -131,26 +131,26 @@
|
|||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"jsonld": "8.3.3",
|
"jsonld": "8.3.3",
|
||||||
"jsrsasign": "11.1.0",
|
"jsrsasign": "11.1.0",
|
||||||
"juice": "11.0.0",
|
"juice": "11.0.1",
|
||||||
"meilisearch": "0.48.2",
|
"meilisearch": "0.49.0",
|
||||||
"mfm-js": "0.24.0",
|
"mfm-js": "0.24.0",
|
||||||
"microformats-parser": "2.0.2",
|
"microformats-parser": "2.0.2",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"ms": "3.0.0-canary.1",
|
"ms": "3.0.0-canary.1",
|
||||||
"nanoid": "5.1.0",
|
"nanoid": "5.1.5",
|
||||||
"nested-property": "4.0.0",
|
"nested-property": "4.0.0",
|
||||||
"node-fetch": "3.3.2",
|
"node-fetch": "3.3.2",
|
||||||
"nodemailer": "6.10.0",
|
"nodemailer": "6.10.0",
|
||||||
"nsfwjs": "4.2.0",
|
"nsfwjs": "4.2.0",
|
||||||
"oauth": "0.10.0",
|
"oauth": "0.10.2",
|
||||||
"oauth2orize": "1.12.0",
|
"oauth2orize": "1.12.0",
|
||||||
"oauth2orize-pkce": "0.1.2",
|
"oauth2orize-pkce": "0.1.2",
|
||||||
"os-utils": "0.0.14",
|
"os-utils": "0.0.14",
|
||||||
"otpauth": "9.3.6",
|
"otpauth": "9.3.6",
|
||||||
"parse5": "7.2.1",
|
"parse5": "7.2.1",
|
||||||
"pg": "8.13.3",
|
"pg": "8.14.1",
|
||||||
"pkce-challenge": "4.1.0",
|
"pkce-challenge": "4.1.0",
|
||||||
"probe-image-size": "7.2.3",
|
"probe-image-size": "7.2.3",
|
||||||
"promise-limit": "2.7.0",
|
"promise-limit": "2.7.0",
|
||||||
@@ -163,8 +163,8 @@
|
|||||||
"reflect-metadata": "0.2.2",
|
"reflect-metadata": "0.2.2",
|
||||||
"rename": "1.0.4",
|
"rename": "1.0.4",
|
||||||
"rss-parser": "3.13.0",
|
"rss-parser": "3.13.0",
|
||||||
"rxjs": "7.8.1",
|
"rxjs": "7.8.2",
|
||||||
"sanitize-html": "2.14.0",
|
"sanitize-html": "2.15.0",
|
||||||
"secure-json-parse": "3.0.2",
|
"secure-json-parse": "3.0.2",
|
||||||
"sharp": "0.33.5",
|
"sharp": "0.33.5",
|
||||||
"slacc": "0.0.10",
|
"slacc": "0.0.10",
|
||||||
@@ -173,14 +173,14 @@
|
|||||||
"systeminformation": "5.25.11",
|
"systeminformation": "5.25.11",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tmp": "0.2.3",
|
"tmp": "0.2.3",
|
||||||
"tsc-alias": "1.8.10",
|
"tsc-alias": "1.8.11",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typeorm": "0.3.20",
|
"typeorm": "0.3.21",
|
||||||
"typescript": "5.7.3",
|
"typescript": "5.8.2",
|
||||||
"ulid": "2.3.0",
|
"ulid": "2.4.0",
|
||||||
"vary": "1.1.2",
|
"vary": "1.1.2",
|
||||||
"web-push": "3.6.7",
|
"web-push": "3.6.7",
|
||||||
"ws": "8.18.0",
|
"ws": "8.18.1",
|
||||||
"xev": "3.0.2"
|
"xev": "3.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
"@types/jsrsasign": "10.5.15",
|
"@types/jsrsasign": "10.5.15",
|
||||||
"@types/mime-types": "2.1.4",
|
"@types/mime-types": "2.1.4",
|
||||||
"@types/ms": "0.7.34",
|
"@types/ms": "0.7.34",
|
||||||
"@types/node": "22.13.4",
|
"@types/node": "22.13.11",
|
||||||
"@types/nodemailer": "6.4.17",
|
"@types/nodemailer": "6.4.17",
|
||||||
"@types/oauth": "0.9.6",
|
"@types/oauth": "0.9.6",
|
||||||
"@types/oauth2orize": "1.11.5",
|
"@types/oauth2orize": "1.11.5",
|
||||||
@@ -223,9 +223,9 @@
|
|||||||
"@types/tmp": "0.2.6",
|
"@types/tmp": "0.2.6",
|
||||||
"@types/vary": "1.1.3",
|
"@types/vary": "1.1.3",
|
||||||
"@types/web-push": "3.6.4",
|
"@types/web-push": "3.6.4",
|
||||||
"@types/ws": "8.5.14",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||||
"@typescript-eslint/parser": "8.24.0",
|
"@typescript-eslint/parser": "8.27.0",
|
||||||
"aws-sdk-client-mock": "4.1.0",
|
"aws-sdk-client-mock": "4.1.0",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
@@ -235,7 +235,6 @@
|
|||||||
"jest-mock": "29.7.0",
|
"jest-mock": "29.7.0",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"pid-port": "1.0.2",
|
"pid-port": "1.0.2",
|
||||||
"simple-oauth2": "5.1.0",
|
"simple-oauth2": "5.1.0"
|
||||||
"vite": "6.2.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,10 @@
|
|||||||
|
|
||||||
import * as fs from 'node:fs';
|
import * as fs from 'node:fs';
|
||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
import { dirname, join, resolve } from 'node:path';
|
import { dirname, resolve } from 'node:path';
|
||||||
import * as yaml from 'js-yaml';
|
import * as yaml from 'js-yaml';
|
||||||
import * as Sentry from '@sentry/node';
|
import * as Sentry from '@sentry/node';
|
||||||
import locale from '../../../locales/index.js';
|
|
||||||
import type { RedisOptions } from 'ioredis';
|
import type { RedisOptions } from 'ioredis';
|
||||||
import type { Manifest, ManifestChunk } from 'vite';
|
|
||||||
import type { ILocale } from '../../../locales/index.js';
|
|
||||||
|
|
||||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||||
host: string;
|
host: string;
|
||||||
@@ -188,12 +185,9 @@ export type Config = {
|
|||||||
authUrl: string;
|
authUrl: string;
|
||||||
driveUrl: string;
|
driveUrl: string;
|
||||||
userAgent: string;
|
userAgent: string;
|
||||||
localeEntries: Record<string, string>;
|
frontendEntry: string;
|
||||||
errorLocaleMessages: Record<string, ILocale>;
|
|
||||||
configEntry: ManifestChunk;
|
|
||||||
frontendEntry: ManifestChunk;
|
|
||||||
frontendManifestExists: boolean;
|
frontendManifestExists: boolean;
|
||||||
frontendEmbedEntry: ManifestChunk;
|
frontendEmbedEntry: string;
|
||||||
frontendEmbedManifestExists: boolean;
|
frontendEmbedManifestExists: boolean;
|
||||||
mediaProxy: string;
|
mediaProxy: string;
|
||||||
externalMediaProxyEnabled: boolean;
|
externalMediaProxyEnabled: boolean;
|
||||||
@@ -235,23 +229,12 @@ export function loadConfig(): Config {
|
|||||||
|
|
||||||
const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json');
|
const frontendManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_vite_/manifest.json');
|
||||||
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
|
const frontendEmbedManifestExists = fs.existsSync(_dirname + '/../../../built/_frontend_embed_vite_/manifest.json');
|
||||||
const frontendManifest: Manifest = frontendManifestExists
|
const frontendManifest = frontendManifestExists ?
|
||||||
? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
|
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_vite_/manifest.json`, 'utf-8'))
|
||||||
: Object.entries(locale).reduce<Record<string, ManifestChunk>>((a, [k]) => {
|
: { 'src/_boot_.ts': { file: 'src/_boot_.ts' } };
|
||||||
a[`locale:${k}`] = { file: `locale:${k}` };
|
const frontendEmbedManifest = frontendEmbedManifestExists ?
|
||||||
return a;
|
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
|
||||||
}, {
|
: { 'src/boot.ts': { file: 'src/boot.ts' } };
|
||||||
'src/_boot_.ts': { file: 'src/_boot_.ts' },
|
|
||||||
'../frontend-shared/js/config.ts': { file: join('@fs', _dirname.slice(1), '../../frontend-shared/js/config.ts') },
|
|
||||||
});
|
|
||||||
const frontendEmbedManifest: Manifest = frontendEmbedManifestExists
|
|
||||||
? JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_frontend_embed_vite_/manifest.json`, 'utf-8'))
|
|
||||||
: Object.entries(locale).reduce<Record<string, ManifestChunk>>((a, [k]) => {
|
|
||||||
a[`locale:${k}`] = { file: `locale:${k}` };
|
|
||||||
return a;
|
|
||||||
}, {
|
|
||||||
'src/boot.ts': { file: 'src/boot.ts' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
|
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
|
||||||
|
|
||||||
@@ -327,20 +310,6 @@ export function loadConfig(): Config {
|
|||||||
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
|
config.videoThumbnailGenerator.endsWith('/') ? config.videoThumbnailGenerator.substring(0, config.videoThumbnailGenerator.length - 1) : config.videoThumbnailGenerator
|
||||||
: null,
|
: null,
|
||||||
userAgent: `Misskey/${version} (${config.url})`,
|
userAgent: `Misskey/${version} (${config.url})`,
|
||||||
localeEntries: Object.entries<ManifestChunk>(frontendManifest).reduce<Record<string, string>>((a, [k, v]) => {
|
|
||||||
if (k.startsWith('locale:')) {
|
|
||||||
a[k.slice('locale:'.length)] = v.file;
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}, {}),
|
|
||||||
errorLocaleMessages: Object.entries(locale).reduce<Record<string, ILocale>>((a, [k, v]) => {
|
|
||||||
a[k] = {
|
|
||||||
_bootErrors: v._bootErrors,
|
|
||||||
reload: v.reload,
|
|
||||||
};
|
|
||||||
return a;
|
|
||||||
}, {}),
|
|
||||||
configEntry: frontendManifest['../frontend-shared/js/config.ts'],
|
|
||||||
frontendEntry: frontendManifest['src/_boot_.ts'],
|
frontendEntry: frontendManifest['src/_boot_.ts'],
|
||||||
frontendManifestExists: frontendManifestExists,
|
frontendManifestExists: frontendManifestExists,
|
||||||
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],
|
frontendEmbedEntry: frontendEmbedManifest['src/boot.ts'],
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as fs from 'node:fs/promises';
|
import * as fs from 'node:fs/promises';
|
||||||
|
import { WritableStream } from 'node:stream/web';
|
||||||
import type { PathLike } from 'node:fs';
|
import type { PathLike } from 'node:fs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -391,10 +391,10 @@ export class ApiCallService implements OnApplicationShutdown {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ep.meta.requireRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
|
if (ep.meta.requiredRolePolicy != null && (this.meta.rootUserId !== user!.id)) {
|
||||||
const myRoles = await this.roleService.getUserRoles(user!.id);
|
const myRoles = await this.roleService.getUserRoles(user!.id);
|
||||||
const policies = await this.roleService.getUserPolicies(user!.id);
|
const policies = await this.roleService.getUserPolicies(user!.id);
|
||||||
if (!policies[ep.meta.requireRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
|
if (!policies[ep.meta.requiredRolePolicy] && !myRoles.some(r => r.isAdministrator)) {
|
||||||
throw new ApiError({
|
throw new ApiError({
|
||||||
message: 'You are not assigned to a required role.',
|
message: 'You are not assigned to a required role.',
|
||||||
code: 'ROLE_PERMISSION_DENIED',
|
code: 'ROLE_PERMISSION_DENIED',
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface IEndpointMetaBase {
|
|||||||
*/
|
*/
|
||||||
readonly requireAdmin?: boolean;
|
readonly requireAdmin?: boolean;
|
||||||
|
|
||||||
readonly requireRolePolicy?: KeyOf<'RolePolicies'>;
|
readonly requiredRolePolicy?: KeyOf<'RolePolicies'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 引っ越し済みのユーザーによるリクエストを禁止するか
|
* 引っ越し済みのユーザーによるリクエストを禁止するか
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageAvatarDecorations',
|
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||||
kind: 'write:admin:avatar-decorations',
|
kind: 'write:admin:avatar-decorations',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageAvatarDecorations',
|
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||||
kind: 'write:admin:avatar-decorations',
|
kind: 'write:admin:avatar-decorations',
|
||||||
errors: {
|
errors: {
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageAvatarDecorations',
|
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||||
kind: 'read:admin:avatar-decorations',
|
kind: 'read:admin:avatar-decorations',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageAvatarDecorations',
|
requiredRolePolicy: 'canManageAvatarDecorations',
|
||||||
kind: 'write:admin:avatar-decorations',
|
kind: 'write:admin:avatar-decorations',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { QueueService } from '@/core/QueueService.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const paramDef = {
|
export const paramDef = {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'read:admin:emoji',
|
kind: 'read:admin:emoji',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'read:admin:emoji',
|
kind: 'read:admin:emoji',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'write:admin:emoji',
|
kind: 'write:admin:emoji',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import { ApiError } from '../../error.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canImportAntennas',
|
requiredRolePolicy: 'canImportAntennas',
|
||||||
prohibitMoved: true,
|
prohibitMoved: true,
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canImportBlocking',
|
requiredRolePolicy: 'canImportBlocking',
|
||||||
prohibitMoved: true,
|
prohibitMoved: true,
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canImportFollowing',
|
requiredRolePolicy: 'canImportFollowing',
|
||||||
prohibitMoved: true,
|
prohibitMoved: true,
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canImportMuting',
|
requiredRolePolicy: 'canImportMuting',
|
||||||
prohibitMoved: true,
|
prohibitMoved: true,
|
||||||
|
|
||||||
limit: {
|
limit: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { ApiError } from '../../error.js';
|
|||||||
export const meta = {
|
export const meta = {
|
||||||
secure: true,
|
secure: true,
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canImportUserLists',
|
requiredRolePolicy: 'canImportUserLists',
|
||||||
prohibitMoved: true,
|
prohibitMoved: true,
|
||||||
limit: {
|
limit: {
|
||||||
duration: ms('1hour'),
|
duration: ms('1hour'),
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export const meta = {
|
|||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canInvite',
|
requiredRolePolicy: 'canInvite',
|
||||||
kind: 'write:invite-codes',
|
kind: 'write:invite-codes',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const meta = {
|
|||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canInvite',
|
requiredRolePolicy: 'canInvite',
|
||||||
kind: 'write:invite-codes',
|
kind: 'write:invite-codes',
|
||||||
|
|
||||||
errors: {
|
errors: {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const meta = {
|
|||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canInvite',
|
requiredRolePolicy: 'canInvite',
|
||||||
kind: 'read:invite-codes',
|
kind: 'read:invite-codes',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export const meta = {
|
|||||||
tags: ['meta'],
|
tags: ['meta'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canInvite',
|
requiredRolePolicy: 'canInvite',
|
||||||
kind: 'read:invite-codes',
|
kind: 'read:invite-codes',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export const meta = {
|
|||||||
tags: ['admin'],
|
tags: ['admin'],
|
||||||
|
|
||||||
requireCredential: true,
|
requireCredential: true,
|
||||||
requireRolePolicy: 'canManageCustomEmojis',
|
requiredRolePolicy: 'canManageCustomEmojis',
|
||||||
kind: 'read:admin:emoji',
|
kind: 'read:admin:emoji',
|
||||||
|
|
||||||
res: {
|
res: {
|
||||||
|
|||||||
@@ -32,9 +32,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
|
if (!localStorage.hasOwnProperty('locale')) {
|
||||||
const supportedLangs = LANGS;
|
const supportedLangs = LANGS;
|
||||||
let lang = localStorage.getItem('lang');
|
let lang = localStorage.getItem('lang');
|
||||||
if (!supportedLangs.includes(lang)) {
|
if (lang == null || !supportedLangs.includes(lang)) {
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
if (supportedLangs.includes(navigator.language)) {
|
||||||
lang = navigator.language;
|
lang = navigator.language;
|
||||||
} else {
|
} else {
|
||||||
@@ -45,11 +46,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await import(`/vite/${LOCALES[lang]}`)
|
const metaRes = await window.fetch('/api/meta', {
|
||||||
.catch(async e => {
|
method: 'POST',
|
||||||
console.error(e);
|
body: JSON.stringify({}),
|
||||||
renderError('LOCALE_FETCH', e);
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
if (metaRes.status !== 200) {
|
||||||
|
renderError('META_FETCH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const meta = await metaRes.json();
|
||||||
|
const v = meta.version;
|
||||||
|
if (v == null) {
|
||||||
|
renderError('META_FETCH_V');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for https://github.com/misskey-dev/misskey/issues/10202
|
||||||
|
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
||||||
|
console.error('invalid lang value detected!!!', typeof lang, lang);
|
||||||
|
lang = 'en-US';
|
||||||
|
}
|
||||||
|
|
||||||
|
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||||
|
if (localRes.status === 200) {
|
||||||
|
localStorage.setItem('lang', lang);
|
||||||
|
localStorage.setItem('locale', await localRes.text());
|
||||||
|
localStorage.setItem('localeVersion', v);
|
||||||
|
} else {
|
||||||
|
renderError('LOCALE_FETCH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Script
|
//#region Script
|
||||||
@@ -83,21 +115,10 @@
|
|||||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportedLangs = LANGS;
|
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||||
let lang = localStorage.getItem('lang');
|
|
||||||
if (!supportedLangs.includes(lang)) {
|
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
|
||||||
lang = navigator.language;
|
|
||||||
} else {
|
|
||||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
|
||||||
|
|
||||||
// Fallback
|
const title = locale?._bootErrors?.title || 'Failed to initialize Misskey';
|
||||||
if (lang == null) lang = 'en-US';
|
const reload = locale?.reload || 'Reload';
|
||||||
}
|
|
||||||
}
|
|
||||||
const { locale } = await import(`/vite/${CONFIG_ENTRY}`).catch(() => ({ locale: {} }));
|
|
||||||
const title = locale._bootErrors.title;
|
|
||||||
const reload = locale.reload;
|
|
||||||
|
|
||||||
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
|
document.body.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M12 9v4" /><path d="M12 16v.01" /></svg>
|
||||||
<div class="message">${title}</div>
|
<div class="message">${title}</div>
|
||||||
|
|||||||
@@ -23,9 +23,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Detect language & fetch translations
|
//#region Detect language & fetch translations
|
||||||
|
if (!localStorage.hasOwnProperty('locale')) {
|
||||||
const supportedLangs = LANGS;
|
const supportedLangs = LANGS;
|
||||||
let lang = localStorage.getItem('lang');
|
let lang = localStorage.getItem('lang');
|
||||||
if (!supportedLangs.includes(lang)) {
|
if (lang == null || !supportedLangs.includes(lang)) {
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
if (supportedLangs.includes(navigator.language)) {
|
||||||
lang = navigator.language;
|
lang = navigator.language;
|
||||||
} else {
|
} else {
|
||||||
@@ -36,11 +37,42 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await import(`/vite/${LOCALES[lang]}`)
|
const metaRes = await window.fetch('/api/meta', {
|
||||||
.catch(async e => {
|
method: 'POST',
|
||||||
console.error(e);
|
body: JSON.stringify({}),
|
||||||
renderError('LOCALE_FETCH', e);
|
credentials: 'omit',
|
||||||
|
cache: 'no-cache',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
if (metaRes.status !== 200) {
|
||||||
|
renderError('META_FETCH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const meta = await metaRes.json();
|
||||||
|
const v = meta.version;
|
||||||
|
if (v == null) {
|
||||||
|
renderError('META_FETCH_V');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for https://github.com/misskey-dev/misskey/issues/10202
|
||||||
|
if (lang == null || lang.toString == null || lang.toString() === 'null') {
|
||||||
|
console.error('invalid lang value detected!!!', typeof lang, lang);
|
||||||
|
lang = 'en-US';
|
||||||
|
}
|
||||||
|
|
||||||
|
const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`);
|
||||||
|
if (localRes.status === 200) {
|
||||||
|
localStorage.setItem('lang', lang);
|
||||||
|
localStorage.setItem('locale', await localRes.text());
|
||||||
|
localStorage.setItem('localeVersion', v);
|
||||||
|
} else {
|
||||||
|
renderError('LOCALE_FETCH');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
//#region Script
|
//#region Script
|
||||||
@@ -119,25 +151,21 @@
|
|||||||
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
await new Promise(resolve => window.addEventListener('DOMContentLoaded', resolve));
|
||||||
}
|
}
|
||||||
|
|
||||||
const supportedLangs = LANGS;
|
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||||
let lang = localStorage.getItem('lang');
|
|
||||||
if (!supportedLangs.includes(lang)) {
|
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
|
||||||
lang = navigator.language;
|
|
||||||
} else {
|
|
||||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
|
||||||
|
|
||||||
// Fallback
|
const messages = Object.assign({
|
||||||
if (lang == null) lang = 'en-US';
|
title: 'Failed to initialize Misskey',
|
||||||
}
|
solution: 'The following actions may solve the problem.',
|
||||||
}
|
solution1: 'Update your os and browser',
|
||||||
const { locale } = await import(`/vite/${CONFIG_ENTRY}`).catch(() => ({
|
solution2: 'Disable an adblocker',
|
||||||
locale: {
|
solution3: 'Clear the browser cache',
|
||||||
_bootErrors: {},
|
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
|
||||||
},
|
otherOption: 'Other options',
|
||||||
}));
|
otherOption1: 'Clear preferences and cache',
|
||||||
const messages = locale._bootErrors;
|
otherOption2: 'Start the simple client',
|
||||||
const reload = locale.reload;
|
otherOption3: 'Start the repair tool',
|
||||||
|
}, locale?._bootErrors || {});
|
||||||
|
const reload = locale?.reload || 'Reload';
|
||||||
|
|
||||||
let errorsElement = document.getElementById('errors');
|
let errorsElement = document.getElementById('errors');
|
||||||
|
|
||||||
|
|||||||
@@ -6,22 +6,23 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const supportedLangs = LANGS;
|
const locale = JSON.parse(localStorage.getItem('locale') || '{}');
|
||||||
let lang = localStorage.getItem('lang');
|
|
||||||
if (!supportedLangs.includes(lang)) {
|
|
||||||
if (supportedLangs.includes(navigator.language)) {
|
|
||||||
lang = navigator.language;
|
|
||||||
} else {
|
|
||||||
lang = supportedLangs.find(x => x.split('-')[0] === navigator.language);
|
|
||||||
|
|
||||||
// Fallback
|
const messages = Object.assign({
|
||||||
if (lang == null) lang = 'en-US';
|
title: 'Failed to initialize Misskey',
|
||||||
}
|
serverError: 'If reloading after a period of time does not resolve the problem, contact the server administrator with the following ERROR ID.',
|
||||||
}
|
solution: 'The following actions may solve the problem.',
|
||||||
const locale = ERROR_MESSAGES[lang];
|
solution1: 'Update your os and browser',
|
||||||
const messages = locale._bootErrors;
|
solution2: 'Disable an adblocker',
|
||||||
const reload = locale.reload;
|
solution3: 'Clear the browser cache',
|
||||||
|
solution4: '(Tor Browser) Set dom.webaudio.enabled to true',
|
||||||
|
otherOption: 'Other options',
|
||||||
|
otherOption1: 'Clear preferences and cache',
|
||||||
|
otherOption2: 'Start the simple client',
|
||||||
|
otherOption3: 'Start the repair tool',
|
||||||
|
}, locale?._bootErrors || {});
|
||||||
|
const reload = locale?.reload || 'Reload';
|
||||||
|
|
||||||
const reloadEls = document.querySelectorAll('[data-i18n-reload]');
|
const reloadEls = document.querySelectorAll('[data-i18n-reload]');
|
||||||
for (const el of reloadEls) {
|
for (const el of reloadEls) {
|
||||||
|
|||||||
@@ -41,8 +41,6 @@ html(class='embed')
|
|||||||
script.
|
script.
|
||||||
var VERSION = "#{version}";
|
var VERSION = "#{version}";
|
||||||
var CLIENT_ENTRY = "#{entry.file}";
|
var CLIENT_ENTRY = "#{entry.file}";
|
||||||
var CONFIG_ENTRY = "#{config.configEntry.file}";
|
|
||||||
var LOCALES = JSON.parse(`!{JSON.stringify(config.localeEntries)}`);
|
|
||||||
|
|
||||||
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
||||||
!= metaJson
|
!= metaJson
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ html
|
|||||||
script.
|
script.
|
||||||
var VERSION = "#{version}";
|
var VERSION = "#{version}";
|
||||||
var CLIENT_ENTRY = "#{entry.file}";
|
var CLIENT_ENTRY = "#{entry.file}";
|
||||||
var CONFIG_ENTRY = "#{config.configEntry.file}";
|
|
||||||
var LOCALES = JSON.parse(`!{JSON.stringify(config.localeEntries)}`);
|
|
||||||
|
|
||||||
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
script(type='application/json' id='misskey_meta' data-generated-at=now)
|
||||||
!= metaJson
|
!= metaJson
|
||||||
|
|||||||
@@ -27,9 +27,6 @@ html
|
|||||||
style
|
style
|
||||||
include ../error.css
|
include ../error.css
|
||||||
|
|
||||||
script.
|
|
||||||
var ERROR_MESSAGES = JSON.parse(`!{JSON.stringify(config.errorLocaleMessages)}`);
|
|
||||||
|
|
||||||
script
|
script
|
||||||
include ../error.js
|
include ../error.js
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"@rollup/pluginutils": "5.1.4",
|
"@rollup/pluginutils": "5.1.4",
|
||||||
"@tabler/icons-webfont": "3.31.0",
|
"@tabler/icons-webfont": "3.31.0",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.3",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"astring": "1.9.0",
|
"astring": "1.9.0",
|
||||||
"buraha": "0.0.1",
|
"buraha": "0.0.1",
|
||||||
@@ -25,16 +25,16 @@
|
|||||||
"misskey-js": "workspace:*",
|
"misskey-js": "workspace:*",
|
||||||
"frontend-shared": "workspace:*",
|
"frontend-shared": "workspace:*",
|
||||||
"punycode.js": "2.3.1",
|
"punycode.js": "2.3.1",
|
||||||
"rollup": "4.34.9",
|
"rollup": "4.36.0",
|
||||||
"sass": "1.85.1",
|
"sass": "1.86.0",
|
||||||
"shiki": "3.1.0",
|
"shiki": "3.2.1",
|
||||||
"tinycolor2": "1.6.0",
|
"tinycolor2": "1.6.0",
|
||||||
"tsc-alias": "1.8.11",
|
"tsc-alias": "1.8.11",
|
||||||
"tsconfig-paths": "4.2.0",
|
"tsconfig-paths": "4.2.0",
|
||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"uuid": "11.1.0",
|
"uuid": "11.1.0",
|
||||||
"json5": "2.2.3",
|
"json5": "2.2.3",
|
||||||
"vite": "6.2.1",
|
"vite": "6.2.2",
|
||||||
"vue": "3.5.13"
|
"vue": "3.5.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -42,26 +42,26 @@
|
|||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"@types/micromatch": "4.0.9",
|
"@types/micromatch": "4.0.9",
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.11",
|
||||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/ws": "8.18.0",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||||
"@typescript-eslint/parser": "8.26.0",
|
"@typescript-eslint/parser": "8.27.0",
|
||||||
"@vitest/coverage-v8": "3.0.8",
|
"@vitest/coverage-v8": "3.0.9",
|
||||||
"@vue/runtime-core": "3.5.13",
|
"@vue/runtime-core": "3.5.13",
|
||||||
"acorn": "8.14.1",
|
"acorn": "8.14.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-vue": "10.0.0",
|
"eslint-plugin-vue": "10.0.0",
|
||||||
"fast-glob": "3.3.3",
|
"fast-glob": "3.3.3",
|
||||||
"happy-dom": "17.3.0",
|
"happy-dom": "17.4.4",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
"msw": "2.7.3",
|
"msw": "2.7.3",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"prettier": "3.5.3",
|
"prettier": "3.5.3",
|
||||||
"start-server-and-test": "2.0.10",
|
"start-server-and-test": "2.0.11",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vue-component-type-helpers": "2.2.8",
|
"vue-component-type-helpers": "2.2.8",
|
||||||
"vue-eslint-parser": "10.1.1",
|
"vue-eslint-parser": "10.1.1",
|
||||||
|
|||||||
@@ -13,17 +13,18 @@ import { createApp, defineAsyncComponent } from 'vue';
|
|||||||
import defaultLightTheme from '@@/themes/l-light.json5';
|
import defaultLightTheme from '@@/themes/l-light.json5';
|
||||||
import defaultDarkTheme from '@@/themes/d-dark.json5';
|
import defaultDarkTheme from '@@/themes/d-dark.json5';
|
||||||
import { MediaProxy } from '@@/js/media-proxy.js';
|
import { MediaProxy } from '@@/js/media-proxy.js';
|
||||||
import { url, version, locale, lang, updateLocale } from '@@/js/config.js';
|
|
||||||
import { parseEmbedParams } from '@@/js/embed-page.js';
|
|
||||||
import type { Theme } from '@/theme.js';
|
|
||||||
import { applyTheme, assertIsTheme } from '@/theme.js';
|
import { applyTheme, assertIsTheme } from '@/theme.js';
|
||||||
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
import { fetchCustomEmojis } from '@/custom-emojis.js';
|
||||||
import { DI } from '@/di.js';
|
import { DI } from '@/di.js';
|
||||||
import { serverMetadata } from '@/server-metadata.js';
|
import { serverMetadata } from '@/server-metadata.js';
|
||||||
|
import { url, version, locale, lang, updateLocale } from '@@/js/config.js';
|
||||||
|
import { parseEmbedParams } from '@@/js/embed-page.js';
|
||||||
import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
|
import { postMessageToParentWindow, setIframeId } from '@/post-message.js';
|
||||||
import { serverContext } from '@/server-context.js';
|
import { serverContext } from '@/server-context.js';
|
||||||
import { i18n, updateI18n } from '@/i18n.js';
|
import { i18n, updateI18n } from '@/i18n.js';
|
||||||
|
|
||||||
|
import type { Theme } from '@/theme.js';
|
||||||
|
|
||||||
console.log('Misskey Embed');
|
console.log('Misskey Embed');
|
||||||
|
|
||||||
//#region Embedパラメータの取得・パース
|
//#region Embedパラメータの取得・パース
|
||||||
@@ -70,6 +71,22 @@ if (embedParams.colorMode === 'dark') {
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region Detect language & fetch translations
|
||||||
|
const localeVersion = localStorage.getItem('localeVersion');
|
||||||
|
const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
|
||||||
|
if (localeOutdated) {
|
||||||
|
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
||||||
|
if (res.status === 200) {
|
||||||
|
const newLocale = await res.text();
|
||||||
|
const parsedNewLocale = JSON.parse(newLocale);
|
||||||
|
localStorage.setItem('locale', newLocale);
|
||||||
|
localStorage.setItem('localeVersion', version);
|
||||||
|
updateLocale(parsedNewLocale);
|
||||||
|
updateI18n(parsedNewLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
// サイズの制限
|
// サイズの制限
|
||||||
document.documentElement.style.maxWidth = '500px';
|
document.documentElement.style.maxWidth = '500px';
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ export const apiUrl = location.origin + '/api';
|
|||||||
export const wsOrigin = location.origin;
|
export const wsOrigin = location.origin;
|
||||||
export const lang = localStorage.getItem('lang') ?? 'en-US';
|
export const lang = localStorage.getItem('lang') ?? 'en-US';
|
||||||
export const langs = _LANGS_;
|
export const langs = _LANGS_;
|
||||||
export let locale: Locale;
|
const preParseLocale = localStorage.getItem('locale');
|
||||||
|
export let locale: Locale = preParseLocale ? JSON.parse(preParseLocale) : null;
|
||||||
export const version = _VERSION_;
|
export const version = _VERSION_;
|
||||||
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
|
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
|
||||||
export const ui = localStorage.getItem('ui');
|
export const ui = localStorage.getItem('ui');
|
||||||
|
|||||||
@@ -109,12 +109,6 @@ export const ROLE_POLICIES = [
|
|||||||
'canImportUserLists',
|
'canImportUserLists',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// なんか動かない
|
|
||||||
//export const CURRENT_STICKY_TOP = Symbol('CURRENT_STICKY_TOP');
|
|
||||||
//export const CURRENT_STICKY_BOTTOM = Symbol('CURRENT_STICKY_BOTTOM');
|
|
||||||
export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
|
|
||||||
export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
|
|
||||||
|
|
||||||
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
|
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg';
|
||||||
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
|
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg';
|
||||||
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
|
export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg';
|
||||||
|
|||||||
@@ -21,10 +21,10 @@
|
|||||||
"lint": "pnpm typecheck && pnpm eslint"
|
"lint": "pnpm typecheck && pnpm eslint"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.11",
|
||||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||||
"@typescript-eslint/parser": "8.26.0",
|
"@typescript-eslint/parser": "8.27.0",
|
||||||
"esbuild": "0.25.0",
|
"esbuild": "0.25.1",
|
||||||
"eslint-plugin-vue": "10.0.0",
|
"eslint-plugin-vue": "10.0.0",
|
||||||
"nodemon": "3.1.9",
|
"nodemon": "3.1.9",
|
||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ function initLocalStorage() {
|
|||||||
...userDetailed(),
|
...userDetailed(),
|
||||||
policies: {},
|
policies: {},
|
||||||
}));
|
}));
|
||||||
|
localStorage.setItem('locale', JSON.stringify(locale));
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize({
|
initialize({
|
||||||
@@ -69,17 +70,13 @@ queueMicrotask(() => {
|
|||||||
import('../src/theme.js'),
|
import('../src/theme.js'),
|
||||||
import('../src/preferences.js'),
|
import('../src/preferences.js'),
|
||||||
import('../src/os.js'),
|
import('../src/os.js'),
|
||||||
import('../src/i18n.js'),
|
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os]) => {
|
||||||
import('../../frontend-shared/js/config.js'),
|
|
||||||
]).then(([{ default: components }, { default: directives }, { default: widgets }, { applyTheme }, { prefer }, os, { updateI18n }, { updateLocale }]) => {
|
|
||||||
setup((app) => {
|
setup((app) => {
|
||||||
moduleInitialized = true;
|
moduleInitialized = true;
|
||||||
if (app[appInitialized]) {
|
if (app[appInitialized]) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app[appInitialized] = true;
|
app[appInitialized] = true;
|
||||||
updateLocale(locale);
|
|
||||||
updateI18n(locale);
|
|
||||||
loadTheme(applyTheme);
|
loadTheme(applyTheme);
|
||||||
components(app);
|
components(app);
|
||||||
directives(app);
|
directives(app);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"@syuilo/aiscript": "0.19.0",
|
"@syuilo/aiscript": "0.19.0",
|
||||||
"@tabler/icons-webfont": "3.31.0",
|
"@tabler/icons-webfont": "3.31.0",
|
||||||
"@twemoji/parser": "15.1.1",
|
"@twemoji/parser": "15.1.1",
|
||||||
"@vitejs/plugin-vue": "5.2.1",
|
"@vitejs/plugin-vue": "5.2.3",
|
||||||
"@vue/compiler-sfc": "3.5.13",
|
"@vue/compiler-sfc": "3.5.13",
|
||||||
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
|
||||||
"analytics": "0.8.16",
|
"analytics": "0.8.16",
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"canvas-confetti": "1.9.3",
|
"canvas-confetti": "1.9.3",
|
||||||
"chart.js": "4.4.8",
|
"chart.js": "4.4.8",
|
||||||
"chartjs-adapter-date-fns": "3.0.0",
|
"chartjs-adapter-date-fns": "3.0.0",
|
||||||
"chartjs-chart-matrix": "2.0.1",
|
"chartjs-chart-matrix": "2.1.1",
|
||||||
"chartjs-plugin-gradient": "0.6.1",
|
"chartjs-plugin-gradient": "0.6.1",
|
||||||
"chartjs-plugin-zoom": "2.2.0",
|
"chartjs-plugin-zoom": "2.2.0",
|
||||||
"chromatic": "11.27.0",
|
"chromatic": "11.27.0",
|
||||||
@@ -60,10 +60,10 @@
|
|||||||
"misskey-reversi": "workspace:*",
|
"misskey-reversi": "workspace:*",
|
||||||
"photoswipe": "5.4.4",
|
"photoswipe": "5.4.4",
|
||||||
"punycode.js": "2.3.1",
|
"punycode.js": "2.3.1",
|
||||||
"rollup": "4.34.9",
|
"rollup": "4.36.0",
|
||||||
"sanitize-html": "2.14.0",
|
"sanitize-html": "2.15.0",
|
||||||
"sass": "1.85.1",
|
"sass": "1.86.0",
|
||||||
"shiki": "3.1.0",
|
"shiki": "3.2.1",
|
||||||
"strict-event-emitter-types": "2.0.0",
|
"strict-event-emitter-types": "2.0.0",
|
||||||
"textarea-caret": "3.1.0",
|
"textarea-caret": "3.1.0",
|
||||||
"three": "0.174.0",
|
"three": "0.174.0",
|
||||||
@@ -74,54 +74,54 @@
|
|||||||
"typescript": "5.8.2",
|
"typescript": "5.8.2",
|
||||||
"uuid": "11.1.0",
|
"uuid": "11.1.0",
|
||||||
"v-code-diff": "1.13.1",
|
"v-code-diff": "1.13.1",
|
||||||
"vite": "6.2.1",
|
"vite": "6.2.2",
|
||||||
"vue": "3.5.13",
|
"vue": "3.5.13",
|
||||||
"vuedraggable": "next",
|
"vuedraggable": "next",
|
||||||
"wanakana": "5.3.1"
|
"wanakana": "5.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@misskey-dev/summaly": "5.2.0",
|
"@misskey-dev/summaly": "5.2.0",
|
||||||
"@storybook/addon-actions": "8.6.4",
|
"@storybook/addon-actions": "8.6.7",
|
||||||
"@storybook/addon-essentials": "8.6.4",
|
"@storybook/addon-essentials": "8.6.7",
|
||||||
"@storybook/addon-interactions": "8.6.4",
|
"@storybook/addon-interactions": "8.6.7",
|
||||||
"@storybook/addon-links": "8.6.4",
|
"@storybook/addon-links": "8.6.7",
|
||||||
"@storybook/addon-mdx-gfm": "8.6.4",
|
"@storybook/addon-mdx-gfm": "8.6.7",
|
||||||
"@storybook/addon-storysource": "8.6.4",
|
"@storybook/addon-storysource": "8.6.7",
|
||||||
"@storybook/blocks": "8.6.4",
|
"@storybook/blocks": "8.6.7",
|
||||||
"@storybook/components": "8.6.4",
|
"@storybook/components": "8.6.7",
|
||||||
"@storybook/core-events": "8.6.4",
|
"@storybook/core-events": "8.6.7",
|
||||||
"@storybook/manager-api": "8.6.4",
|
"@storybook/manager-api": "8.6.7",
|
||||||
"@storybook/preview-api": "8.6.4",
|
"@storybook/preview-api": "8.6.7",
|
||||||
"@storybook/react": "8.6.4",
|
"@storybook/react": "8.6.7",
|
||||||
"@storybook/react-vite": "8.6.4",
|
"@storybook/react-vite": "8.6.7",
|
||||||
"@storybook/test": "8.6.4",
|
"@storybook/test": "8.6.7",
|
||||||
"@storybook/theming": "8.6.4",
|
"@storybook/theming": "8.6.7",
|
||||||
"@storybook/types": "8.6.4",
|
"@storybook/types": "8.6.7",
|
||||||
"@storybook/vue3": "8.6.4",
|
"@storybook/vue3": "8.6.7",
|
||||||
"@storybook/vue3-vite": "8.6.4",
|
"@storybook/vue3-vite": "8.6.7",
|
||||||
"@testing-library/vue": "8.1.0",
|
"@testing-library/vue": "8.1.0",
|
||||||
"@types/canvas-confetti": "1.9.0",
|
"@types/canvas-confetti": "1.9.0",
|
||||||
"@types/estree": "1.0.6",
|
"@types/estree": "1.0.6",
|
||||||
"@types/matter-js": "0.19.8",
|
"@types/matter-js": "0.19.8",
|
||||||
"@types/micromatch": "4.0.9",
|
"@types/micromatch": "4.0.9",
|
||||||
"@types/node": "22.13.9",
|
"@types/node": "22.13.11",
|
||||||
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
"@types/punycode.js": "npm:@types/punycode@2.1.4",
|
||||||
"@types/sanitize-html": "2.13.0",
|
"@types/sanitize-html": "2.13.0",
|
||||||
"@types/seedrandom": "3.0.8",
|
"@types/seedrandom": "3.0.8",
|
||||||
"@types/throttle-debounce": "5.0.2",
|
"@types/throttle-debounce": "5.0.2",
|
||||||
"@types/tinycolor2": "1.4.6",
|
"@types/tinycolor2": "1.4.6",
|
||||||
"@types/ws": "8.18.0",
|
"@types/ws": "8.18.0",
|
||||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||||
"@typescript-eslint/parser": "8.26.0",
|
"@typescript-eslint/parser": "8.27.0",
|
||||||
"@vitest/coverage-v8": "3.0.8",
|
"@vitest/coverage-v8": "3.0.9",
|
||||||
"@vue/runtime-core": "3.5.13",
|
"@vue/runtime-core": "3.5.13",
|
||||||
"acorn": "8.14.1",
|
"acorn": "8.14.1",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"cypress": "14.1.0",
|
"cypress": "14.2.0",
|
||||||
"eslint-plugin-import": "2.31.0",
|
"eslint-plugin-import": "2.31.0",
|
||||||
"eslint-plugin-vue": "10.0.0",
|
"eslint-plugin-vue": "10.0.0",
|
||||||
"fast-glob": "3.3.3",
|
"fast-glob": "3.3.3",
|
||||||
"happy-dom": "17.3.0",
|
"happy-dom": "17.4.4",
|
||||||
"intersection-observer": "0.12.2",
|
"intersection-observer": "0.12.2",
|
||||||
"micromatch": "4.0.8",
|
"micromatch": "4.0.8",
|
||||||
"msw": "2.7.3",
|
"msw": "2.7.3",
|
||||||
@@ -131,12 +131,12 @@
|
|||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-dom": "19.0.0",
|
"react-dom": "19.0.0",
|
||||||
"seedrandom": "3.0.5",
|
"seedrandom": "3.0.5",
|
||||||
"start-server-and-test": "2.0.10",
|
"start-server-and-test": "2.0.11",
|
||||||
"storybook": "8.6.4",
|
"storybook": "8.6.7",
|
||||||
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
|
||||||
"vite-node": "3.0.8",
|
"vite-node": "3.0.9",
|
||||||
"vite-plugin-turbosnap": "1.0.3",
|
"vite-plugin-turbosnap": "1.0.3",
|
||||||
"vitest": "3.0.8",
|
"vitest": "3.0.9",
|
||||||
"vitest-fetch-mock": "0.4.5",
|
"vitest-fetch-mock": "0.4.5",
|
||||||
"vue-component-type-helpers": "2.2.8",
|
"vue-component-type-helpers": "2.2.8",
|
||||||
"vue-eslint-parser": "10.1.1",
|
"vue-eslint-parser": "10.1.1",
|
||||||
|
|||||||
@@ -78,6 +78,22 @@ export async function common(createVue: () => App<Element>) {
|
|||||||
}
|
}
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|
||||||
|
//#region Detect language & fetch translations
|
||||||
|
const localeVersion = miLocalStorage.getItem('localeVersion');
|
||||||
|
const localeOutdated = (localeVersion == null || localeVersion !== version || locale == null);
|
||||||
|
if (localeOutdated) {
|
||||||
|
const res = await window.fetch(`/assets/locales/${lang}.${version}.json`);
|
||||||
|
if (res.status === 200) {
|
||||||
|
const newLocale = await res.text();
|
||||||
|
const parsedNewLocale = JSON.parse(newLocale);
|
||||||
|
miLocalStorage.setItem('locale', newLocale);
|
||||||
|
miLocalStorage.setItem('localeVersion', version);
|
||||||
|
updateLocale(parsedNewLocale);
|
||||||
|
updateI18n(parsedNewLocale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
// タッチデバイスでCSSの:hoverを機能させる
|
// タッチデバイスでCSSの:hoverを機能させる
|
||||||
window.document.addEventListener('touchend', () => {}, { passive: true });
|
window.document.addEventListener('touchend', () => {}, { passive: true });
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
:leaveToClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_leaveTo : $style.swipeAnimationRight_leaveTo"
|
:leaveToClass="transitionName === 'swipeAnimationLeft' ? $style.swipeAnimationLeft_leaveTo : $style.swipeAnimationRight_leaveTo"
|
||||||
:style="`--swipe: ${pullDistance}px;`"
|
:style="`--swipe: ${pullDistance}px;`"
|
||||||
>
|
>
|
||||||
<!-- 【注意】slot内の最上位要素に動的にkeyを設定すること -->
|
<div :key="tabModel">
|
||||||
<!-- 各最上位要素にユニークなkeyの指定がないとTransitionがうまく動きません -->
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, provide, inject, ref, watch, useTemplateRef } from 'vue';
|
import { onMounted, onUnmounted, provide, inject, ref, watch, useTemplateRef } from 'vue';
|
||||||
import { CURRENT_STICKY_BOTTOM, CURRENT_STICKY_TOP } from '@@/js/const.js';
|
import { DI } from '@/di.js';
|
||||||
import type { Ref } from 'vue';
|
|
||||||
|
|
||||||
const rootEl = useTemplateRef('rootEl');
|
const rootEl = useTemplateRef('rootEl');
|
||||||
const headerEl = useTemplateRef('headerEl');
|
const headerEl = useTemplateRef('headerEl');
|
||||||
@@ -32,13 +31,13 @@ const footerEl = useTemplateRef('footerEl');
|
|||||||
|
|
||||||
const headerHeight = ref<string | undefined>();
|
const headerHeight = ref<string | undefined>();
|
||||||
const childStickyTop = ref(0);
|
const childStickyTop = ref(0);
|
||||||
const parentStickyTop = inject<Ref<number>>(CURRENT_STICKY_TOP, ref(0));
|
const parentStickyTop = inject(DI.currentStickyTop, ref(0));
|
||||||
provide(CURRENT_STICKY_TOP, childStickyTop);
|
provide(DI.currentStickyTop, childStickyTop);
|
||||||
|
|
||||||
const footerHeight = ref<string | undefined>();
|
const footerHeight = ref<string | undefined>();
|
||||||
const childStickyBottom = ref(0);
|
const childStickyBottom = ref(0);
|
||||||
const parentStickyBottom = inject<Ref<number>>(CURRENT_STICKY_BOTTOM, ref(0));
|
const parentStickyBottom = inject(DI.currentStickyBottom, ref(0));
|
||||||
provide(CURRENT_STICKY_BOTTOM, childStickyBottom);
|
provide(DI.currentStickyBottom, childStickyBottom);
|
||||||
|
|
||||||
const calc = () => {
|
const calc = () => {
|
||||||
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
// コンポーネントが表示されてないけどKeepAliveで残ってる場合などは null になる
|
||||||
|
|||||||
44
packages/frontend/src/components/global/PageWithHeader.vue
Normal file
44
packages/frontend/src/components/global/PageWithHeader.vue
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<!--
|
||||||
|
SPDX-FileCopyrightText: syuilo and misskey-project
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div :class="[$style.root, reversed ? '_pageScrollableReversed' : '_pageScrollable']">
|
||||||
|
<MkStickyContainer>
|
||||||
|
<template #header><MkPageHeader v-model:tab="tab" :actions="actions" :tabs="tabs"/></template>
|
||||||
|
<div :class="$style.body">
|
||||||
|
<slot></slot>
|
||||||
|
</div>
|
||||||
|
<template #footer><slot name="footer"></slot></template>
|
||||||
|
</MkStickyContainer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import type { PageHeaderItem } from '@/types/page-header.js';
|
||||||
|
import type { Tab } from './MkPageHeader.tabs.vue';
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<{
|
||||||
|
tabs?: Tab[];
|
||||||
|
actions?: PageHeaderItem[] | null;
|
||||||
|
thin?: boolean;
|
||||||
|
hideTitle?: boolean;
|
||||||
|
displayMyAvatar?: boolean;
|
||||||
|
reversed?: boolean;
|
||||||
|
}>(), {
|
||||||
|
tabs: () => ([] as Tab[]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = defineModel<string>('tab');
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" module>
|
||||||
|
.root {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -25,6 +25,8 @@ import MkPageHeader from './global/MkPageHeader.vue';
|
|||||||
import MkSpacer from './global/MkSpacer.vue';
|
import MkSpacer from './global/MkSpacer.vue';
|
||||||
import MkStickyContainer from './global/MkStickyContainer.vue';
|
import MkStickyContainer from './global/MkStickyContainer.vue';
|
||||||
import MkLazy from './global/MkLazy.vue';
|
import MkLazy from './global/MkLazy.vue';
|
||||||
|
import PageWithHeader from './global/PageWithHeader.vue';
|
||||||
|
import PageWithAnimBg from './global/PageWithAnimBg.vue';
|
||||||
import SearchMarker from './global/SearchMarker.vue';
|
import SearchMarker from './global/SearchMarker.vue';
|
||||||
import SearchLabel from './global/SearchLabel.vue';
|
import SearchLabel from './global/SearchLabel.vue';
|
||||||
import SearchKeyword from './global/SearchKeyword.vue';
|
import SearchKeyword from './global/SearchKeyword.vue';
|
||||||
@@ -60,6 +62,8 @@ export const components = {
|
|||||||
MkSpacer: MkSpacer,
|
MkSpacer: MkSpacer,
|
||||||
MkStickyContainer: MkStickyContainer,
|
MkStickyContainer: MkStickyContainer,
|
||||||
MkLazy: MkLazy,
|
MkLazy: MkLazy,
|
||||||
|
PageWithHeader: PageWithHeader,
|
||||||
|
PageWithAnimBg: PageWithAnimBg,
|
||||||
SearchMarker: SearchMarker,
|
SearchMarker: SearchMarker,
|
||||||
SearchLabel: SearchLabel,
|
SearchLabel: SearchLabel,
|
||||||
SearchKeyword: SearchKeyword,
|
SearchKeyword: SearchKeyword,
|
||||||
@@ -89,6 +93,8 @@ declare module '@vue/runtime-core' {
|
|||||||
MkSpacer: typeof MkSpacer;
|
MkSpacer: typeof MkSpacer;
|
||||||
MkStickyContainer: typeof MkStickyContainer;
|
MkStickyContainer: typeof MkStickyContainer;
|
||||||
MkLazy: typeof MkLazy;
|
MkLazy: typeof MkLazy;
|
||||||
|
PageWithHeader: typeof PageWithHeader;
|
||||||
|
PageWithAnimBg: typeof PageWithAnimBg;
|
||||||
SearchMarker: typeof SearchMarker;
|
SearchMarker: typeof SearchMarker;
|
||||||
SearchLabel: typeof SearchLabel;
|
SearchLabel: typeof SearchLabel;
|
||||||
SearchKeyword: typeof SearchKeyword;
|
SearchKeyword: typeof SearchKeyword;
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ export const DI = {
|
|||||||
mock: Symbol() as InjectionKey<boolean>,
|
mock: Symbol() as InjectionKey<boolean>,
|
||||||
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>,
|
pageMetadata: Symbol() as InjectionKey<Ref<Record<string, any>>>,
|
||||||
viewId: Symbol() as InjectionKey<string>,
|
viewId: Symbol() as InjectionKey<string>,
|
||||||
|
currentStickyTop: Symbol() as InjectionKey<Ref<number>>,
|
||||||
|
currentStickyBottom: Symbol() as InjectionKey<Ref<number>>,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ export type Keys = (
|
|||||||
'fontSize' |
|
'fontSize' |
|
||||||
'ui' |
|
'ui' |
|
||||||
'ui_temp' |
|
'ui_temp' |
|
||||||
'locale' | // DEPRECATED
|
'locale' |
|
||||||
'localeVersion' | // DEPRECATED
|
'localeVersion' |
|
||||||
'theme' |
|
'theme' |
|
||||||
'themeId' |
|
'themeId' |
|
||||||
'customCss' |
|
'customCss' |
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<div style="overflow: clip;">
|
<div style="overflow: clip;">
|
||||||
<MkSpacer :contentMax="600" :marginMin="20">
|
<MkSpacer :contentMax="600" :marginMin="20">
|
||||||
<div class="_gaps_m znqjceqz">
|
<div class="_gaps_m znqjceqz">
|
||||||
@@ -130,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20">
|
<MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20">
|
||||||
<XOverview/>
|
<XOverview/>
|
||||||
@@ -20,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkInstanceStats/>
|
<MkInstanceStats/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :contentMax="1200">
|
<MkSpacer :contentMax="1200">
|
||||||
<MkAchievements :user="$i"/>
|
<MkAchievements :user="$i"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer v-if="file" :contentMax="600" :marginMin="16" :marginMax="32">
|
<MkSpacer v-if="file" :contentMax="600" :marginMin="16" :marginMax="32">
|
||||||
<div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m">
|
<div v-if="tab === 'overview'" class="cxqhhsmd _gaps_m">
|
||||||
<a class="thumbnail" :href="file.url" target="_blank">
|
<a class="thumbnail" :href="file.url" target="_blank">
|
||||||
@@ -67,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkObjectView>
|
</MkObjectView>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
|
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<div v-if="tab === 'overview'" class="_gaps_m">
|
<div v-if="tab === 'overview'" class="_gaps_m">
|
||||||
@@ -208,7 +207,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
|
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
|
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
|
||||||
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
|
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
|
||||||
@@ -14,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,15 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
|
|
||||||
<MkSpacer :contentMax="500">
|
<MkSpacer :contentMax="500">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/>
|
<MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<Transition
|
<Transition
|
||||||
:enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''"
|
:enterActiveClass="prefer.s.animation ? $style.fadeEnterActive : ''"
|
||||||
@@ -44,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkLoading v-else/>
|
<MkLoading v-else/>
|
||||||
</Transition>
|
</Transition>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div :key="tab" class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo>
|
<MkInfo v-if="$i && $i.hasUnreadAnnouncement && tab === 'current'" warn>{{ i18n.ts.youHaveUnreadAnnouncements }}</MkInfo>
|
||||||
<MkPagination ref="paginationEl" :key="tab" v-slot="{items}" :pagination="tab === 'current' ? paginationCurrent : paginationPast" class="_gaps">
|
<MkPagination ref="paginationEl" :key="tab" v-slot="{items}" :pagination="tab === 'current' ? paginationCurrent : paginationPast" class="_gaps">
|
||||||
<section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement">
|
<section v-for="announcement in items" :key="announcement.id" class="_panel" :class="$style.announcement">
|
||||||
@@ -43,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div ref="rootEl">
|
<div ref="rootEl">
|
||||||
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
<div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
|
||||||
@@ -20,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
@@ -30,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="500">
|
<MkSpacer :contentMax="500">
|
||||||
<div v-if="state == 'fetch-session-error'">
|
<div v-if="state == 'fetch-session-error'">
|
||||||
<p>{{ i18n.ts.somethingHappened }}</p>
|
<p>{{ i18n.ts.somethingHappened }}</p>
|
||||||
@@ -38,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkSignin @login="onLogin"/>
|
<MkSignin @login="onLogin"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<div :class="$style.decorations">
|
<div :class="$style.decorations">
|
||||||
@@ -22,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div v-if="channelId == null || channel != null" class="_gaps_m">
|
<div v-if="channelId == null || channel != null" class="_gaps_m">
|
||||||
<MkInput v-model="name">
|
<MkInput v-model="name">
|
||||||
@@ -65,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<MkSpacer :contentMax="700">
|
||||||
<MkSpacer :contentMax="700" :class="$style.main">
|
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="channel && tab === 'overview'" key="overview" class="_gaps">
|
<div v-if="channel && tab === 'overview'" class="_gaps">
|
||||||
<div class="_panel" :class="$style.bannerContainer">
|
<div class="_panel" :class="$style.bannerContainer">
|
||||||
<XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/>
|
<XChannelFollowButton :channel="channel" :full="true" :class="$style.subscribe"/>
|
||||||
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary :class="$style.favorite" @click="unfavorite()"><i class="ti ti-star"></i></MkButton>
|
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary :class="$style.favorite" @click="unfavorite()"><i class="ti ti-star"></i></MkButton>
|
||||||
@@ -33,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="channel && tab === 'timeline'" key="timeline" class="_gaps">
|
<div v-if="channel && tab === 'timeline'" class="_gaps">
|
||||||
<MkInfo v-if="channel.isArchived" warn>{{ i18n.ts.thisChannelArchived }}</MkInfo>
|
<MkInfo v-if="channel.isArchived" warn>{{ i18n.ts.thisChannelArchived }}</MkInfo>
|
||||||
|
|
||||||
<!-- スマホ・タブレットの場合、キーボードが表示されると投稿が見づらくなるので、デスクトップ場合のみ自動でフォーカスを当てる -->
|
<!-- スマホ・タブレットの場合、キーボードが表示されると投稿が見づらくなるので、デスクトップ場合のみ自動でフォーカスを当てる -->
|
||||||
@@ -41,10 +40,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
|
|
||||||
<MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/>
|
<MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'featured'" key="featured">
|
<div v-else-if="tab === 'featured'">
|
||||||
<MkNotes :pagination="featuredPagination"/>
|
<MkNotes :pagination="featuredPagination"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'search'" key="search">
|
<div v-else-if="tab === 'search'">
|
||||||
<div v-if="notesSearchAvailable" class="_gaps">
|
<div v-if="notesSearchAvailable" class="_gaps">
|
||||||
<div>
|
<div>
|
||||||
<MkInput v-model="searchQuery" @enter="search()">
|
<MkInput v-model="searchQuery" @enter="search()">
|
||||||
@@ -69,7 +68,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -271,10 +270,6 @@ definePage(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.main {
|
|
||||||
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
-webkit-backdrop-filter: var(--MI-blur, blur(15px));
|
||||||
backdrop-filter: var(--MI-blur, blur(15px));
|
backdrop-filter: var(--MI-blur, blur(15px));
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="1200">
|
<MkSpacer :contentMax="1200">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'search'" key="search" :class="$style.searchRoot">
|
<div v-if="tab === 'search'" :class="$style.searchRoot">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
|
<MkInput v-model="searchQuery" :large="true" :autofocus="true" type="search" @enter="search">
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
@@ -25,28 +24,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkChannelList :key="key" :pagination="channelPagination"/>
|
<MkChannelList :key="key" :pagination="channelPagination"/>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="tab === 'featured'" key="featured">
|
<div v-if="tab === 'featured'">
|
||||||
<MkPagination v-slot="{items}" :pagination="featuredPagination">
|
<MkPagination v-slot="{items}" :pagination="featuredPagination">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'favorites'" key="favorites">
|
<div v-else-if="tab === 'favorites'">
|
||||||
<MkPagination v-slot="{items}" :pagination="favoritesPagination">
|
<MkPagination v-slot="{items}" :pagination="favoritesPagination">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'following'" key="following">
|
<div v-else-if="tab === 'following'">
|
||||||
<MkPagination v-slot="{items}" :pagination="followingPagination">
|
<MkPagination v-slot="{items}" :pagination="followingPagination">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
<MkChannelPreview v-for="channel in items" :key="channel.id" :channel="channel"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'owned'" key="owned">
|
<div v-else-if="tab === 'owned'">
|
||||||
<MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
|
<MkButton class="new" @click="create()"><i class="ti ti-plus"></i></MkButton>
|
||||||
<MkPagination v-slot="{items}" :pagination="ownedPagination">
|
<MkPagination v-slot="{items}" :pagination="ownedPagination">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
@@ -56,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,12 +4,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkClickerGame/>
|
<MkClickerGame/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions">
|
||||||
<template #header><MkPageHeader :actions="headerActions"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div v-if="clip" class="_gaps">
|
<div v-if="clip" class="_gaps">
|
||||||
<div class="_panel">
|
<div class="_panel">
|
||||||
@@ -27,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkNotes :pagination="pagination" :detail="true"/>
|
<MkNotes :pagination="pagination" :detail="true"/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :contentMax="600" :marginMin="20">
|
<MkSpacer :contentMax="600" :marginMin="20">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkKeyValue :copy="instance.maintainerName">
|
<MkKeyValue :copy="instance.maintainerName">
|
||||||
@@ -31,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkKeyValue>
|
</MkKeyValue>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="ogwlenmc">
|
<div class="ogwlenmc">
|
||||||
<div v-if="tab === 'local'" class="local">
|
<div v-if="tab === 'local'" class="local">
|
||||||
@@ -68,7 +67,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<MkSpacer v-if="tab === 'info'" key="info" :contentMax="800">
|
<MkSpacer v-if="tab === 'info'" :contentMax="800">
|
||||||
<XFileInfo :fileId="fileId"/>
|
<XFileInfo :fileId="fileId"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
||||||
<MkSpacer v-else-if="tab === 'notes'" key="notes" :contentMax="800">
|
<MkSpacer v-else-if="tab === 'notes'" :contentMax="800">
|
||||||
<XNotes :fileId="fileId"/>
|
<XNotes :fileId="fileId"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
|
|||||||
@@ -4,20 +4,19 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'featured'" key="featured">
|
<div v-if="tab === 'featured'">
|
||||||
<XFeatured/>
|
<XFeatured/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'users'" key="users">
|
<div v-else-if="tab === 'users'">
|
||||||
<XUsers/>
|
<XUsers/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'roles'" key="roles">
|
<div v-else-if="tab === 'roles'">
|
||||||
<XRoles/>
|
<XRoles/>
|
||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkPagination :pagination="pagination">
|
<MkPagination :pagination="pagination">
|
||||||
<template #empty>
|
<template #empty>
|
||||||
@@ -22,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInput v-model="title">
|
<MkInput v-model="title">
|
||||||
@@ -37,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'featured'" key="featured">
|
<div v-if="tab === 'featured'">
|
||||||
<MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
|
<MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/>
|
<MkFlashPreview v-for="flash in items" :key="flash.id" :flash="flash"/>
|
||||||
@@ -16,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'my'" key="my">
|
<div v-else-if="tab === 'my'">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton>
|
<MkButton gradate rounded style="margin: 0 auto;" @click="create()"><i class="ti ti-plus"></i></MkButton>
|
||||||
<MkPagination v-slot="{items}" :pagination="myFlashsPagination">
|
<MkPagination v-slot="{items}" :pagination="myFlashsPagination">
|
||||||
@@ -27,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'liked'" key="liked">
|
<div v-else-if="tab === 'liked'">
|
||||||
<MkPagination v-slot="{items}" :pagination="likedFlashsPagination">
|
<MkPagination v-slot="{items}" :pagination="likedFlashsPagination">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/>
|
<MkFlashPreview v-for="like in items" :key="like.flash.id" :flash="like.flash"/>
|
||||||
@@ -36,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
||||||
<div v-if="flash" :key="flash.id">
|
<div v-if="flash" :key="flash.id">
|
||||||
@@ -58,7 +57,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkLoading v-else/>
|
<MkLoading v-else/>
|
||||||
</Transition>
|
</Transition>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div :key="tab" class="_gaps">
|
|
||||||
<MkPagination ref="paginationComponent" :pagination="pagination">
|
<MkPagination ref="paginationComponent" :pagination="pagination">
|
||||||
<template #empty>
|
<template #empty>
|
||||||
<div class="_fullinfo">
|
<div class="_fullinfo">
|
||||||
@@ -37,10 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
|
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init" class="_gaps">
|
<FormSuspense :p="init" class="_gaps">
|
||||||
<MkInput v-model="title">
|
<MkInput v-model="title">
|
||||||
@@ -34,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</FormSuspense>
|
</FormSuspense>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="1400">
|
<MkSpacer :contentMax="1400">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'explore'" key="explore">
|
<div v-if="tab === 'explore'">
|
||||||
<MkFoldableSection class="_margin">
|
<MkFoldableSection class="_margin">
|
||||||
<template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template>
|
<template #header><i class="ti ti-clock"></i>{{ i18n.ts.recentPosts }}</template>
|
||||||
<MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true">
|
<MkPagination v-slot="{items}" :pagination="recentPostsPagination" :disableAutoLoad="true">
|
||||||
@@ -26,14 +25,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkPagination>
|
</MkPagination>
|
||||||
</MkFoldableSection>
|
</MkFoldableSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'liked'" key="liked">
|
<div v-else-if="tab === 'liked'">
|
||||||
<MkPagination v-slot="{items}" :pagination="likedPostsPagination">
|
<MkPagination v-slot="{items}" :pagination="likedPostsPagination">
|
||||||
<div :class="$style.items">
|
<div :class="$style.items">
|
||||||
<MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/>
|
<MkGalleryPostPreview v-for="like in items" :key="like.id" :post="like.post" class="post"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'my'" key="my">
|
<div v-else-if="tab === 'my'">
|
||||||
<MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA>
|
<MkA to="/gallery/new" class="_link" style="margin: 16px;"><i class="ti ti-plus"></i> {{ i18n.ts.postToGallery }}</MkA>
|
||||||
<MkPagination v-slot="{items}" :pagination="myPostsPagination">
|
<MkPagination v-slot="{items}" :pagination="myPostsPagination">
|
||||||
<div :class="$style.items">
|
<div :class="$style.items">
|
||||||
@@ -43,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="1000" :marginMin="16" :marginMax="32">
|
<MkSpacer :contentMax="1000" :marginMin="16" :marginMax="32">
|
||||||
<div class="_root">
|
<div class="_root">
|
||||||
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
||||||
@@ -59,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<div class="_panel" :class="$style.link">
|
<div class="_panel" :class="$style.link">
|
||||||
@@ -20,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkPageWithAnimBg>
|
<PageWithAnimBg>
|
||||||
<MkSpacer :contentMax="550" :marginMax="50">
|
<MkSpacer :contentMax="550" :marginMax="50">
|
||||||
<MkLoading v-if="uiPhase === 'fetching'"/>
|
<MkLoading v-if="uiPhase === 'fetching'"/>
|
||||||
<MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()" @cancel="close_()">
|
<MkExtensionInstaller v-else-if="uiPhase === 'confirm' && data" :extension="data" @confirm="install()" @cancel="close_()">
|
||||||
@@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkPageWithAnimBg>
|
</PageWithAnimBg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -57,7 +57,6 @@ import { parseThemeCode, installTheme } from '@/theme.js';
|
|||||||
import { unisonReload } from '@/utility/unison-reload.js';
|
import { unisonReload } from '@/utility/unison-reload.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
|
||||||
|
|
||||||
const uiPhase = ref<'fetching' | 'confirm' | 'error'>('fetching');
|
const uiPhase = ref<'fetching' | 'confirm' | 'error'>('fetching');
|
||||||
const errorKV = ref<{
|
const errorKV = ref<{
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32">
|
<MkSpacer v-if="instance" :contentMax="600" :marginMin="16" :marginMax="32">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'overview'" key="overview" class="_gaps_m">
|
<div v-if="tab === 'overview'" class="_gaps_m">
|
||||||
<div class="fnfelxur">
|
<div class="fnfelxur">
|
||||||
<img :src="faviconUrl" alt="" class="icon"/>
|
<img :src="faviconUrl" alt="" class="icon"/>
|
||||||
<span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span>
|
<span class="name">{{ instance.name || `(${i18n.ts.unknown})` }}</span>
|
||||||
@@ -91,7 +90,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink>
|
<FormLink :to="`https://${host}/manifest.json`" external style="margin-bottom: 8px;">manifest.json</FormLink>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'chart'" key="chart" class="_gaps_m">
|
<div v-else-if="tab === 'chart'" class="_gaps_m">
|
||||||
<div class="cmhjzshl">
|
<div class="cmhjzshl">
|
||||||
<div class="selects">
|
<div class="selects">
|
||||||
<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
|
<MkSelect v-model="chartSrc" style="margin: 0 10px 0 0; flex: 1;">
|
||||||
@@ -116,27 +115,28 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'users'" key="users" class="_gaps_m">
|
<div v-else-if="tab === 'users'" class="_gaps_m">
|
||||||
<MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;">
|
<MkPagination v-slot="{items}" :pagination="usersPagination" style="display: grid; grid-template-columns: repeat(auto-fill,minmax(270px,1fr)); grid-gap: 12px;">
|
||||||
<MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/admin/user/${user.id}`">
|
<MkA v-for="user in items" :key="user.id" v-tooltip.mfm="`Last posted: ${dateString(user.updatedAt)}`" class="user" :to="`/admin/user/${user.id}`">
|
||||||
<MkUserCardMini :user="user"/>
|
<MkUserCardMini :user="user"/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'raw'" key="raw" class="_gaps_m">
|
<div v-else-if="tab === 'raw'" class="_gaps_m">
|
||||||
<MkObjectView tall :value="instance">
|
<MkObjectView tall :value="instance">
|
||||||
</MkObjectView>
|
</MkObjectView>
|
||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, watch } from 'vue';
|
import { ref, computed, watch } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkChart from '@/components/MkChart.vue';
|
|
||||||
import type { ChartSrc } from '@/components/MkChart.vue';
|
import type { ChartSrc } from '@/components/MkChart.vue';
|
||||||
|
import type { Paging } from '@/components/MkPagination.vue';
|
||||||
|
import MkChart from '@/components/MkChart.vue';
|
||||||
import MkObjectView from '@/components/MkObjectView.vue';
|
import MkObjectView from '@/components/MkObjectView.vue';
|
||||||
import FormLink from '@/components/form/link.vue';
|
import FormLink from '@/components/form/link.vue';
|
||||||
import MkLink from '@/components/MkLink.vue';
|
import MkLink from '@/components/MkLink.vue';
|
||||||
@@ -153,7 +153,6 @@ import { definePage } from '@/page.js';
|
|||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
import MkPagination from '@/components/MkPagination.vue';
|
import MkPagination from '@/components/MkPagination.vue';
|
||||||
import type { Paging } from '@/components/MkPagination.vue';
|
|
||||||
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
||||||
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
|
import { getProxiedImageUrlNullable } from '@/utility/media-proxy.js';
|
||||||
import { dateString } from '@/filters/date.js';
|
import { dateString } from '@/filters/date.js';
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader>
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200">
|
<MkSpacer v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
|
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
|
||||||
@@ -30,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer v-if="error != null" :contentMax="1200">
|
<MkSpacer v-if="error != null" :contentMax="1200">
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
|
<img :class="$style.img" :src="serverErrorImageUrl" draggable="false"/>
|
||||||
@@ -15,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<MkSpacer v-else-if="list" :contentMax="700" :class="$style.main">
|
<MkSpacer v-else-if="list" :contentMax="700">
|
||||||
<div v-if="list" class="members _margin">
|
<div v-if="list" class="members _margin">
|
||||||
<div :class="$style.member_text">{{ i18n.ts.members }}</div>
|
<div :class="$style.member_text">{{ i18n.ts.members }}</div>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
@@ -30,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton>
|
<MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton>
|
||||||
<MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton>
|
<MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -107,10 +106,6 @@ definePage(() => ({
|
|||||||
}));
|
}));
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.main {
|
|
||||||
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
|
|
||||||
}
|
|
||||||
|
|
||||||
.userItem {
|
.userItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div v-if="state === 'done'" class="_buttonsCenter">
|
<div v-if="state === 'done'" class="_buttonsCenter">
|
||||||
<MkButton @click="close">{{ i18n.ts.close }}</MkButton>
|
<MkButton @click="close">{{ i18n.ts.close }}</MkButton>
|
||||||
@@ -15,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
<MkLoading/>
|
<MkLoading/>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkPageWithAnimBg>
|
<PageWithAnimBg>
|
||||||
<div :class="$style.formContainer">
|
<div :class="$style.formContainer">
|
||||||
<div :class="$style.form">
|
<div :class="$style.form">
|
||||||
<MkAuthConfirm
|
<MkAuthConfirm
|
||||||
@@ -24,13 +24,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkAuthConfirm>
|
</MkAuthConfirm>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkPageWithAnimBg>
|
</PageWithAnimBg>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, useTemplateRef } from 'vue';
|
import { computed, useTemplateRef } from 'vue';
|
||||||
import * as Misskey from 'misskey-js';
|
import * as Misskey from 'misskey-js';
|
||||||
import MkPageWithAnimBg from '@/components/MkPageWithAnimBg.vue';
|
|
||||||
import MkAuthConfirm from '@/components/MkAuthConfirm.vue';
|
import MkAuthConfirm from '@/components/MkAuthConfirm.vue';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { misskeyApi } from '@/utility/misskey-api.js';
|
import { misskeyApi } from '@/utility/misskey-api.js';
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
|
|
||||||
<MkAntennaEditor @created="onAntennaCreated"/>
|
<MkAntennaEditor @created="onAntennaCreated"/>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,11 +4,9 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
|
|
||||||
<MkAntennaEditor v-if="antenna" :antenna="antenna" @updated="onAntennaUpdated"/>
|
<MkAntennaEditor v-if="antenna" :antenna="antenna" @updated="onAntennaUpdated"/>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div>
|
<div>
|
||||||
<div v-if="antennas.length === 0" class="empty">
|
<div v-if="antennas.length === 0" class="empty">
|
||||||
@@ -24,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,23 +4,22 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'my'" key="my" class="_gaps">
|
<div v-if="tab === 'my'" class="_gaps">
|
||||||
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
|
|
||||||
<MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
|
<MkPagination v-slot="{ items }" ref="pagingComponent" :pagination="pagination" class="_gaps">
|
||||||
<MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/>
|
<MkClipPreview v-for="item in items" :key="item.id" :clip="item" :noUserInfo="true"/>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'favorites'" key="favorites" class="_gaps">
|
<div v-else-if="tab === 'favorites'" class="_gaps">
|
||||||
<MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
|
<MkClipPreview v-for="item in favorites" :key="item.id" :clip="item"/>
|
||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<div v-if="items.length === 0" class="empty">
|
<div v-if="items.length === 0" class="empty">
|
||||||
@@ -25,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,9 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<MkSpacer :contentMax="700">
|
||||||
<MkSpacer :contentMax="700" :class="$style.main">
|
|
||||||
<div v-if="list" class="_gaps">
|
<div v-if="list" class="_gaps">
|
||||||
<MkFolder>
|
<MkFolder>
|
||||||
<template #label>{{ i18n.ts.settings }}</template>
|
<template #label>{{ i18n.ts.settings }}</template>
|
||||||
@@ -49,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</MkFolder>
|
</MkFolder>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
@@ -198,10 +197,6 @@ definePage(() => ({
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.main {
|
|
||||||
min-height: calc(100cqh - (var(--MI-stickyTop, 0px) + var(--MI-stickyBottom, 0px)));
|
|
||||||
}
|
|
||||||
|
|
||||||
.userItem {
|
.userItem {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div>
|
<div>
|
||||||
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
<Transition :name="prefer.s.animation ? 'fade' : ''" mode="out-in">
|
||||||
@@ -44,7 +43,7 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|||||||
@@ -4,33 +4,32 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<PageWithHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs">
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
<MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs">
|
||||||
<div v-if="tab === 'all'" key="all">
|
<div v-if="tab === 'all'">
|
||||||
<XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/>
|
<XNotifications :class="$style.notifications" :excludeTypes="excludeTypes"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'mentions'" key="mention">
|
<div v-else-if="tab === 'mentions'">
|
||||||
<MkNotes :pagination="mentionsPagination"/>
|
<MkNotes :pagination="mentionsPagination"/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="tab === 'directNotes'" key="directNotes">
|
<div v-else-if="tab === 'directNotes'">
|
||||||
<MkNotes :pagination="directNotesPagination"/>
|
<MkNotes :pagination="directNotesPagination"/>
|
||||||
</div>
|
</div>
|
||||||
</MkHorizontalSwipe>
|
</MkHorizontalSwipe>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</MkStickyContainer>
|
</PageWithHeader>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import { notificationTypes } from '@@/js/const.js';
|
||||||
import XNotifications from '@/components/MkNotifications.vue';
|
import XNotifications from '@/components/MkNotifications.vue';
|
||||||
import MkNotes from '@/components/MkNotes.vue';
|
import MkNotes from '@/components/MkNotes.vue';
|
||||||
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
|
||||||
import * as os from '@/os.js';
|
import * as os from '@/os.js';
|
||||||
import { i18n } from '@/i18n.js';
|
import { i18n } from '@/i18n.js';
|
||||||
import { definePage } from '@/page.js';
|
import { definePage } from '@/page.js';
|
||||||
import { notificationTypes } from '@@/js/const.js';
|
|
||||||
|
|
||||||
const tab = ref('all');
|
const tab = ref('all');
|
||||||
const includeTypes = ref<string[] | null>(null);
|
const includeTypes = ref<string[] | null>(null);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user